Módulo 4: Entornos dev, staging y prod en self-hosted
8. Proyecto: tres entornos aislados corriendo
Descripción
Al terminar este proyecto vas a tener, corriendo a la vez en tu máquina, los tres entornos de Cumbre —dev, staging y prod—, cada uno con su propia clave de cifrado, sus credenciales de prueba o reales y su webhook propio, y vas a haber demostrado el aislamiento: que un cambio en un entorno no toca a los otros, y que un secreto cifrado en un entorno no se descifra en otro. Vas a producir el entregable del módulo —los compose files, el .env.example de cada entorno y una prueba de aislamiento documentada— que es la evidencia concreta de que sabes montar el patrón que las ofertas de trabajo piden cuando escriben "experience with staging and production environments".
Esto importa porque es donde todo el módulo deja de ser conceptos y se vuelve algo que corre y que puedes mostrar. Un dueño del sistema no dice "sé de entornos"; enciende tres, y demuestra que están aislados. Este proyecto es tu prueba, para ti mismo y para una entrevista, de que cruzaste la línea: ya no dependes de "probar con cuidado en producción", porque tienes el cuarto acolchonado montado y verificado con tus propias manos.
Conexión con el módulo: este proyecto junta todo. El esqueleto de la lección 3 (environments/{dev,staging,prod}/ con su plano común), los .env con claves distintas de la lección 4, las credenciales de prueba vs. reales de la lección 5, y los valores por entorno con $env de la lección 6, se combinan aquí en un sistema que funciona. La lección 7 te dio el criterio para saber que este patrón self-hosted es la opción correcta para empezar; esta lección lo hace realidad. Y mira hacia adelante: los tres entornos que dejas corriendo son el escenario del Módulo 5 (probar en dev a costo cero) y del Módulo 6 (promover de dev a staging a prod).
Qué vas a construir, en una foto
Antes de la secuencia paso a paso, la foto completa de lo que vas a tener al final, para que sepas hacia dónde vas. Piénsalo como el plano del edificio terminado antes de recorrer los pisos.
cumbre-automations/
├── workflows/
│ └── order-triage.json ← UNA versión, compartida por los tres entornos
├── environments/
│ ├── dev/
│ │ ├── docker-compose.yml ← el plano (idéntico entre entornos)
│ │ ├── .env.example ← el contrato (se versiona)
│ │ └── .env ← valores reales de dev (NO se versiona)
│ ├── staging/
│ │ ├── docker-compose.yml
│ │ ├── .env.example
│ │ └── .env ← valores reales de staging (NO se versiona)
│ └── prod/
│ ├── docker-compose.yml
│ ├── .env.example
│ └── .env ← valores reales de prod (NO se versiona)
├── docs/
│ └── isolation-test.md ← la prueba de aislamiento documentada (entregable)
└── .gitignore ← ignora **/.env, versiona **/.env.example
Y corriendo, tres stacks separados:
| Entorno | Proyecto Compose | Puerto | URL | Clave de cifrado | Credenciales |
|---|---|---|---|---|---|
dev | cumbre-dev | 5678 | http://localhost:5678 | propia (<clave-dev>) | sandbox / local |
staging | cumbre-staging | 5679 | http://localhost:5679 | propia (<clave-staging>) | sandbox / límite bajo |
prod | cumbre-prod | 5680 | http://localhost:5680 | propia (<clave-prod>) | reales |
Tres instancias, tres bases de datos, tres juegos de volúmenes, tres claves distintas, tres puertos. El mismo order-triage.json en las tres. Eso es lo que vas a encender y verificar.
Una nota de recursos antes de empezar: correr tres instancias de n8n con su base de datos a la vez consume memoria. Si tu máquina va justa, levanta los entornos de a uno para probar cada paso, y solo enciende los tres a la vez para la prueba final de aislamiento. No necesitas los tres prendidos todo el tiempo; los necesitas prendidos juntos solo el momento de demostrar que conviven sin tocarse. Y recuerda: tú corres todos estos comandos en tu máquina; la guía no ejecuta nada.
Fase 1 — Preparar la estructura y los .env
Paso 1 — Confirma el esqueleto. Si vienes siguiendo el módulo, ya tienes environments/{dev,staging,prod}/ con el docker-compose.yml (plano común, de la lección 3) y el .env.example (contrato) en cada carpeta. Confírmalo:
ls environments/dev environments/staging environments/prod
Qué esperar: en cada carpeta, al menos docker-compose.yml y .env.example. Si falta algo, vuelve a la lección 3 y recréalo; el plano es el de allí.
Paso 2 — Genera tres claves de cifrado distintas. Una por entorno, con openssl:
openssl rand -hex 32 # córrelo tres veces; guarda cada resultado por separado
Qué esperar: tres cadenas de 64 caracteres, todas diferentes. Anótalas asociadas a su entorno (dev/staging/prod) y guárdalas en tu gestor de contraseñas: son las llaves maestras de cada entorno, y si las pierdes, pierdes acceso a las credenciales de ese entorno.
Paso 3 — Crea el .env real de cada entorno. En cada carpeta, copia el .env.example a .env y rellénalo con los valores de ese entorno. Para dev:
# environments/dev/.env (NO se versiona)
COMPOSE_PROJECT_NAME=cumbre-dev
N8N_PORT=5678
N8N_HOST=localhost
WEBHOOK_URL=http://localhost:5678/
N8N_ENCRYPTION_KEY=<clave-dev> # la primera que generaste
POSTGRES_USER=cumbre
POSTGRES_PASSWORD=<password-dev> # aleatoria, distinta por entorno
POSTGRES_DB=n8n
CRM_BASE_URL=https://sandbox.crm.example
Repite para staging (proyecto cumbre-staging, puerto 5679, WEBHOOK_URL con 5679, su propia clave y contraseña, CRM_BASE_URL de staging) y para prod (proyecto cumbre-prod, puerto 5680, WEBHOOK_URL con 5680, su propia clave, y CRM_BASE_URL=https://crm.cumbre.com).
Qué esperar: tres .env, uno por carpeta, que se parecen en estructura pero difieren en las líneas que importan: nombre de proyecto, puerto, WEBHOOK_URL, clave de cifrado, contraseña de la base y URL del CRM. Esas diferencias son la identidad de cada entorno.
Paso 4 — Verifica que los secretos están fuera del alcance de Git. El reflejo de seguridad, antes de cualquier commit:
git status
Qué esperar: ves los docker-compose.yml y los .env.example, pero ninguno de los tres .env. Si aparece un .env, detente y arregla el .gitignore (**/.env con la excepción !**/.env.example) antes de seguir. Este chequeo no es opcional: es la diferencia entre un repo profesional y una filtración de claves.
Fase 2 — Levantar los tres entornos
Paso 5 — Levanta dev. Entra a su carpeta y arráncalo en segundo plano:
docker compose -f environments/dev/docker-compose.yml --env-file environments/dev/.env -p cumbre-dev up -d
Desmenucemos el comando: -f indica qué docker-compose.yml usar; --env-file indica qué .env leer; -p cumbre-dev fija el nombre del proyecto (la "dirección" que aísla el stack); up -d levanta en segundo plano. (Si prefieres, puedes cd environments/dev y correr solo docker compose up -d, porque Compose toma el .env de la carpeta y usa COMPOSE_PROJECT_NAME; el comando largo es explícito para que se vea qué pasa.)
Qué esperar: Docker crea los volúmenes cumbre-dev_n8n_storage y cumbre-dev_postgres_storage, la red cumbre-dev_default, y arranca los contenedores. La terminal te devuelve el control (por la -d). Abre http://localhost:5678 y te recibe n8n. Esa pantalla es la señal de que dev está vivo.
Paso 6 — Levanta staging y prod. Lo mismo, apuntando a cada carpeta y con su nombre de proyecto:
docker compose -f environments/staging/docker-compose.yml --env-file environments/staging/.env -p cumbre-staging up -d
docker compose -f environments/prod/docker-compose.yml --env-file environments/prod/.env -p cumbre-prod up -d
Qué esperar: dos stacks más, con sus propios volúmenes prefijados (cumbre-staging_*, cumbre-prod_*). staging responde en http://localhost:5679 y prod en http://localhost:5680. Si alguno falla con "port is already allocated", su N8N_PORT choca con otro: revisa que sean 5678/5679/5680 distintos.
Paso 7 — Confirma que los tres corren, aislados. Pídele a Docker la lista de proyectos y de volúmenes:
docker compose ls
docker volume ls
Qué esperar: docker compose ls muestra tres proyectos: cumbre-dev, cumbre-staging, cumbre-prod. docker volume ls muestra volúmenes con los tres prefijos: cumbre-dev_postgres_storage, cumbre-staging_postgres_storage, cumbre-prod_postgres_storage, etc. Ver los tres prefijos separados es la primera evidencia visual del aislamiento: cada entorno tiene sus propios cajones de datos, con su propio nombre, sin compartir ninguno.
Fase 3 — Poblar cada entorno con el workflow y sus credenciales
Paso 8 — Crea las credenciales en cada entorno, con el valor de ese entorno. En dev (http://localhost:5678), crea Cumbre CRM key (Header Auth) con el token sandbox, y Cumbre LLM key con un modelo local de Ollama o una llave de límite bajo. En prod (http://localhost:5680), crea credenciales con el mismo nombre y tipo pero los valores reales. Repite en staging con valores de prueba.
Qué esperar: cada entorno tiene sus dos credenciales, con nombres idénticos entre entornos y valores distintos. Recuerda la disciplina de la lección 5: el nombre es el enchufe común; el valor es lo que cambia por entorno. Y la regla de oro: lo real solo en prod.
Paso 9 — Importa order-triage en los tres. Importa el mismo workflows/order-triage.json en cada instancia. En cada una, abre los nodos con credencial y confirma que quedaron conectados a la credencial correcta de ese entorno.
Qué esperar: el mismo workflow corriendo en los tres entornos, cada uno resolviendo Cumbre CRM key a su propio valor y {{ $env.CRM_BASE_URL }} a la URL de su .env. Un workflow, tres entornos, cero diferencias en el JSON. Si un nodo sale con la credencial faltante, es el caso del id que no coincide (lección 5): selecciónala a mano.
Fase 4 — La prueba de aislamiento (el corazón del proyecto)
Aquí está lo que de verdad demuestra que hiciste bien el trabajo. No basta con que los tres corran; hay que probar que están aislados. Vamos a hacerlo con dos pruebas concretas.
Prueba A — Un cambio en dev no aparece en prod. Con los tres entornos corriendo:
- En
dev(http://localhost:5678), haz un cambio visible y sin riesgo: crea un workflow nuevo llamadoisolation-marker-dev, o renombraorder-triageaorder-triage (editado en dev). Guárdalo. - Ve a
prod(http://localhost:5680) y recarga la lista de workflows.
Qué esperar: en prod, no aparece isolation-marker-dev, y order-triage sigue con su nombre original, sin el "(editado en dev)". El cambio que hiciste en dev vive solo en la base de datos de dev; prod ni se enteró. Esa ausencia es la prueba: los entornos no comparten base de datos, así que un cambio en uno es invisible para el otro. Si el cambio sí apareciera en prod, algo está mal —probablemente comparten volumen o proyecto—, y hay que revisar el COMPOSE_PROJECT_NAME de cada .env.
Prueba B — Un secreto de un entorno no se descifra en otro. Esta prueba demuestra la pared de la clave de cifrado. La forma conceptual, y la forma concreta:
- La forma conceptual (razónalo): la credencial
Cumbre CRM keydedevestá cifrada con<clave-dev>. La instancia deprodusa<clave-prod>, distinta. Por lo tanto, si tomaras el dato cifrado de la credencial dedevy lo metieras en la base deprod,prodno podría descifrarlo: para él sería basura. Las credenciales no cruzan porque las llaves no coinciden. - La forma concreta (demuéstralo, opcional y con cuidado): apaga
dev, cámbiale en el.envlaN8N_ENCRYPTION_KEYpor la deprod(<clave-prod>), y vuelve a levantarlo. Al abrir la credencialCumbre CRM keyendev, n8n ya no la puede descifrar y muestra un error de descifrado, porque la credencial se cifró con<clave-dev>y ahora la instancia usa otra clave. Deshaz el cambio (vuelve a poner<clave-dev>en el.envdedevy reinicia) para recuperar la credencial. Esta demostración es la que graba, en carne propia, por qué la clave se fija y no se cambia —y por qué cada entorno necesita la suya—.
Qué esperar de la Prueba B: ves con tus ojos que cambiar la clave rompe el descifrado, lo que confirma que un secreto cifrado con la clave de un entorno es inservible bajo la clave de otro. Es la razón por la que las credenciales se recrean por entorno (lección 5) y no se copian.
Paso 10 — Documenta la prueba de aislamiento. Este es el entregable que cierra el proyecto. En docs/isolation-test.md, escribe un documento corto que registre lo que probaste. Un esqueleto:
# Prueba de aislamiento de entornos — Cumbre
## Entornos levantados
| Entorno | Proyecto | Puerto | URL |
|---|---|---|---|
| dev | cumbre-dev | 5678 | http://localhost:5678 |
| staging | cumbre-staging | 5679 | http://localhost:5679 |
| prod | cumbre-prod | 5680 | http://localhost:5680 |
## Evidencia de recursos separados
`docker compose ls` muestra tres proyectos; `docker volume ls` muestra
volúmenes con prefijos cumbre-dev_ / cumbre-staging_ / cumbre-prod_.
## Prueba A — cambio en dev no aparece en prod
Creé `isolation-marker-dev` en dev. En prod NO aparece. Los entornos no
comparten base de datos.
## Prueba B — secreto de un entorno no se descifra en otro
Cada entorno tiene su propia N8N_ENCRYPTION_KEY. Una credencial cifrada en
dev no se descifra en prod porque las claves difieren. (Verificado cambiando
la clave de dev por la de prod: la credencial dejó de descifrarse; se revirtió.)
## Conclusión
Los tres entornos están aislados: datos separados, claves separadas,
credenciales separadas. Un cambio en uno no afecta a los otros.
Qué esperar: un documento de una página que cualquiera —un compañero, un entrevistador— puede leer para confirmar, sin encender nada, que montaste entornos de verdad aislados. Ese documento, junto con los compose files y los .env.example, es el entregable del módulo.
Fase 5 — Cerrar con limpieza
Paso 11 — Verifica una última vez que no se coló ningún secreto. Antes de commitear el entregable:
git status
git add -n . # muestra qué se agregaría, sin agregar nada
Qué esperar: en lo que se agregaría ves docker-compose.yml, .env.example, docs/isolation-test.md y workflows/order-triage.json, pero ningún .env, ninguna clave, ninguna credencial. (git add -n es un ensayo: muestra qué haría sin hacerlo.) Si aparece un secreto, no commitees: arregla el .gitignore primero. Este es el mismo reflejo del Módulo 3, y es el último filtro antes de que algo sensible entre a la historia.
Paso 12 — Apaga los entornos cuando termines. Para liberar memoria, apaga cada stack (conservando los datos en los volúmenes):
docker compose -p cumbre-dev down
docker compose -p cumbre-staging down
docker compose -p cumbre-prod down
Qué esperar: los contenedores y las redes se eliminan, pero los volúmenes —con tus workflows, bases de datos y claves— siguen ahí. La próxima vez que levantes los entornos, todo está donde lo dejaste. Apagar no borra: para borrar los datos harían falta down -v, y ese comando se usa con plena conciencia.
Si algo no arranca: diagnóstico rápido
Levantar tres entornos a la vez es donde más cosas pueden salir mal, casi siempre por detalles de configuración, no por conceptos. Antes de los errores de fondo, una guía rápida para los tropiezos de arranque más comunes. Recuerda: estos comandos de diagnóstico son de solo lectura; los corres tú.
Síntoma: "port is already allocated" al levantar el segundo o tercer entorno. Dos entornos piden el mismo puerto de tu máquina. Revisa el N8N_PORT de cada .env: deben ser 5678, 5679, 5680, distintos. Si un entorno anterior quedó a medio apagar, puede seguir ocupando su puerto; ciérralo con docker compose -p <proyecto> down y vuelve a intentar.
Síntoma: n8n no responde en su URL, aunque el comando "terminó bien". El contenedor puede estar arrancando todavía (la base de datos tarda unos segundos en estar sana, y n8n espera por el depends_on). Mira los logs:
docker compose -p cumbre-dev logs -f n8n
logs -f muestra los logs en vivo (la -f los sigue). Qué esperar: líneas de arranque y, al final, un mensaje de que el editor está disponible. Si ves errores de conexión a la base de datos repetidos, revisa que POSTGRES_USER/POSTGRES_PASSWORD coincidan entre la sección postgres y las variables DB_POSTGRESDB_* (salen del mismo .env, así que deberían).
Síntoma: {{ $env.CRM_BASE_URL }} devuelve vacío. O el .env no tiene esa línea, o cambiaste el .env sin reiniciar (las variables se leen al arrancar), o N8N_BLOCK_ENV_ACCESS_IN_NODE está en true. Confirma la línea en el .env, reinicia el entorno, y verifica el acceso a $env.
Síntoma: una credencial aparece "no se puede descifrar" tras reiniciar. La N8N_ENCRYPTION_KEY de ese .env cambió respecto de cuando se creó la credencial (lección 4). Si tienes la clave original, vuelve a ponerla; si la perdiste, recrea la credencial. Verifica que no copiaste el .env de otro entorno encima.
Ver el estado de todos los contenedores de un entorno:
docker compose -p cumbre-dev ps
Qué esperar: los servicios n8n y postgres con estado running (y postgres como healthy). Si alguno está restarting o exited, sus logs (logs) te dicen por qué. Este ps es tu primer vistazo cuando algo "no está" pero no sabes qué.
La mayoría de estos tropiezos se arreglan en un minuto una vez que sabes dónde mirar. La diferencia entre frustrarte y resolverlo es tener el reflejo de ir a los logs y al ps en vez de adivinar. Ese reflejo —diagnosticar con datos, no con corazonadas— es, otra vez, lo que hace un dueño del sistema.
Si tu máquina va justa: la variante secuencial
Correr tres instancias de n8n con tres bases de datos a la vez pide memoria, y no toda máquina la tiene de sobra. Si la tuya se ahoga con los tres prendidos, no abandones el proyecto: hazlo por turnos, que demuestra lo mismo con menos carga.
La idea: la mayoría de los pasos —crear credenciales, importar el workflow, verificar que cada entorno arranca— no necesitan los tres a la vez. Levanta uno, trabájalo, apágalo, levanta el siguiente. Solo la Prueba A de aislamiento (un cambio en dev no aparece en prod) necesita dos entornos prendidos al mismo tiempo, y con dos basta —no hacen falta los tres—. Así:
- Levanta
dev, crea sus credenciales, importaorder-triage, haz el cambio marcador (isolation-marker-dev). Déjalo prendido. - Levanta
prod(ahora hay dos). Comprueba queisolation-marker-devno aparece enprod. Esa es la Prueba A, con solo dos entornos. - Apaga
devpara liberar memoria. Termina de poblarprodcon sus credenciales reales. stagingpuedes montarlo y verificarlo por separado, sin necesidad de que los otros dos estén arriba.
La evidencia de recursos separados (docker volume ls con los tres prefijos) la ves aunque los contenedores estén apagados, porque los volúmenes persisten. Así que puedes levantar y apagar cuando quieras: los cajones de datos de los tres entornos siguen ahí, con sus prefijos, listos para mostrar el aislamiento. La variante secuencial no demuestra menos; solo reparte la carga en el tiempo. El aislamiento es una propiedad de cómo están montados, no de que estén todos prendidos a la vez.
La lista del entregable
Antes de dar el proyecto por terminado, verifica que tienes las tres piezas del entregable, que son las que un entrevistador o un compañero revisaría:
- Los compose files. El
docker-compose.ymlde cada entorno (el plano común), enenvironments/{dev,staging,prod}/. - Los
.env.example. El contrato de configuración de cada entorno, con los nombres de las variables y sin los valores secretos, versionado. - La prueba de aislamiento documentada. El
docs/isolation-test.mdcon los entornos levantados, la evidencia de recursos separados, y las Pruebas A y B. - Ningún secreto. Confirmado con
git status: ningún.env, ninguna clave, ninguna credencial en lo que se versiona.
Si marcaste las cuatro, tienes el entregable completo del módulo: la evidencia de que sabes montar y demostrar entornos aislados, empaquetada de forma que otra persona lo entienda sin que estés presente. Ese "otro puede sin mí" es, otra vez, el estándar que persigue toda la guía.
Errores comunes
Levantar los tres bajo el mismo proyecto y creer que están aislados (conceptual, y anula el proyecto). Qué pasa: alguien olvida cambiar COMPOSE_PROJECT_NAME o el -p, levanta los tres con el mismo nombre de proyecto, y como Docker los trata como el mismo stack, en realidad tiene uno solo que se reemplaza a sí mismo. La Prueba A "falla" porque el cambio en dev sí aparece en "prod" (que es la misma instancia). Cómo detectarlo: docker compose ls muestra un solo proyecto en vez de tres. Cómo corregirlo: un COMPOSE_PROJECT_NAME único por entorno; ese nombre es lo que crea el aislamiento. Sin tres proyectos distintos, no hay tres entornos.
Poner la credencial real en dev durante el proyecto (práctico, y peligroso). Qué pasa: por comodidad, alguien usa la llave real del CRM en dev "solo para que funcione la prueba". Ahora las pruebas de dev tocan datos reales, justo lo que el módulo entero busca evitar. Cómo detectarlo: si el valor de una credencial de dev es el real de producción, hay un problema. Cómo corregirlo: valores sandbox o locales en dev y staging; reales solo en prod. Si ya usaste la real en dev y la expusiste, rótala.
Olvidar reiniciar tras cambiar un .env y pensar que el aislamiento falla (práctico). Qué pasa: alguien cambia un .env (por ejemplo, CRM_BASE_URL) con el entorno corriendo, no ve el cambio reflejado, y concluye que "algo se cruzó entre entornos". En realidad, n8n no releyó el .env porque no reinició. Cómo detectarlo: un cambio en el .env que no aparece, sin haber reiniciado. Cómo corregirlo: tras tocar un .env, docker compose ... down && ... up -d de ese entorno. Las variables se leen al arrancar.
Commitear un .env en la prisa del entregable (práctico, y el más grave). Qué pasa: al final del proyecto, con ganas de "guardar todo", alguien hace git add . y se cuela un .env con claves reales. Cómo detectarlo: git status o git add -n . muestran un .env entre lo que se agregaría. Cómo corregirlo: nunca git add . sin antes confirmar con git status que no hay .env en la lista; confía en el .gitignore de **/.env, y si por lo que sea falla, no commitees hasta arreglarlo. El entregable son los compose files, los .env.example y el isolation-test.md, jamás un .env.
Ejercicios
Ejercicio 1 — Diseña una tercera prueba de aislamiento. Las Pruebas A (cambio en dev no aparece en prod) y B (secreto no cruza) demuestran aislamiento de datos y de claves. Diseña una tercera prueba, distinta, que demuestre otra faceta del aislamiento, y describe qué harías y qué esperarías ver. Pista: piensa en los puertos, o en las credenciales, o en apagar un entorno.
Ver solución
Hay varias buenas respuestas. Tres ejemplos:
-
Prueba de puertos independientes: apaga solo
dev(docker compose -p cumbre-dev down) y confirma questaging(5679) yprod(5680) siguen respondiendo. Esperas ver que apagar un entorno no afecta a los otros: cada uno vive en su propio stack, así que la caída de uno no arrastra a los demás. Es aislamiento de disponibilidad. -
Prueba de credenciales independientes: en
dev, borra la credencialCumbre CRM key. Ve aprody confirma que suCumbre CRM keysigue intacta. Esperas que borrar la credencial en un entorno no toque la del otro, porque viven en bases de datos separadas. -
Prueba de datos de ejecución: ejecuta
order-triagecon un pedido de prueba endevy revisa el historial de ejecuciones. Enprod, el historial no tiene esa ejecución. Esperas que las ejecuciones de un entorno no aparezcan en otro.
Por qué funciona: diseñar tu propia prueba te obliga a entender qué es el aislamiento —recursos separados en todas sus formas: datos, claves, puertos, credenciales, ejecuciones— en vez de repetir las dos pruebas dadas. Un dueño del sistema no solo pasa las pruebas que le dan; inventa las que hacen falta.
Ejercicio 2 — Rastrea el flujo de un valor. Sigue el recorrido completo de la URL del CRM en prod, desde dónde se escribe hasta dónde se usa. Nombra cada lugar por el que pasa: (1) dónde vive el valor real, (2) cómo llega al contenedor de n8n, (3) cómo lo lee el workflow, (4) qué URL final produce para un pedido con customerId=Z-9. Usa CRM_BASE_URL=https://crm.cumbre.com.
Ver solución
(1) El valor real vive en environments/prod/.env, en la línea CRM_BASE_URL=https://crm.cumbre.com. Ese archivo está fuera del repositorio (ignorado por .gitignore).
(2) Al levantar prod con docker compose ... --env-file environments/prod/.env ..., Docker Compose lee ese .env e inyecta CRM_BASE_URL como variable de entorno dentro del contenedor de n8n de prod.
(3) El nodo HTTP Request de order-triage lee la variable con la expresión {{ $env.CRM_BASE_URL }} en su campo "URL", combinándola con la ruta: {{ $env.CRM_BASE_URL }}/customers/{{ $json.customerId }}.
(4) Para un pedido con customerId=Z-9, la URL final es https://crm.cumbre.com/customers/Z-9.
Por qué funciona: rastrear el valor de punta a punta —.env → contenedor → expresión → URL final— confirma que entiendes cómo la configuración de un entorno llega hasta la ejecución del workflow sin estar escrita dentro del JSON. Es el "un workflow, muchos entornos" visto como un flujo completo, no como una idea suelta.
Ejercicio 3 — Defiende tu proyecto en una entrevista. Un entrevistador te dice: "Veo que montaste tres entornos con Docker Compose. Convénceme de que están de verdad aislados y de que sabes por qué cada pieza está donde está." Escribe tu respuesta en un párrafo, tocando al menos: el mecanismo del aislamiento, la clave de cifrado por entorno, dónde viven los secretos, y cómo lo demostrarías en vivo.
Ver solución
No hay una única respuesta; una buena tocaría estos puntos: "Cada entorno corre como un proyecto de Docker Compose distinto —cumbre-dev, cumbre-staging, cumbre-prod—, y ese nombre de proyecto hace que Docker cree volúmenes, contenedores y redes separados para cada uno, así que sus bases de datos no se tocan: es aislamiento por construcción, no por configuración frágil. Cada entorno tiene su propia N8N_ENCRYPTION_KEY, distinta a propósito, de modo que una credencial cifrada en dev no se puede descifrar en prod; eso aísla los secretos entre entornos. Los secretos reales —las llaves del CRM y del modelo— viven solo en el .env de prod, fuera del repositorio, y el workflow los consume sin contenerlos: la referencia de la credencial y las expresiones $env viajan en el JSON, los valores no. Lo demostraría en vivo levantando los tres, haciendo un cambio en dev y mostrando que no aparece en prod, y enseñando con docker volume ls que cada entorno tiene sus propios volúmenes. Todo esto corre en Community, a costo cero; la feature de pago solo agregaría comodidad de interfaz y auditoría, que a esta escala no necesito."
Por qué funciona: la respuesta no dice "monté entornos"; explica el mecanismo (nombre de proyecto), la seguridad (clave por entorno, secretos fuera del repo) y la evidencia (cómo lo demuestra), y ubica la decisión de plan. Esa combinación —cómo funciona, por qué es seguro, cómo lo pruebo, cuánto cuesta— es exactamente lo que separa, en una entrevista, a un dueño del sistema de un constructor de workflows.
Resumen y siguiente paso
En este proyecto encendiste el módulo entero. Levantaste los tres entornos de Cumbre —dev, staging y prod— corriendo a la vez, cada uno como su propio proyecto de Docker Compose (cumbre-dev/cumbre-staging/cumbre-prod), con su puerto (5678/5679/5680), su .env con clave de cifrado y contraseña propias, sus credenciales de prueba o reales, y su WEBHOOK_URL. Importaste el mismo order-triage.json en los tres, con cada entorno resolviendo la credencial y el $env.CRM_BASE_URL a lo suyo. Y —el corazón del proyecto— demostraste el aislamiento con dos pruebas: un cambio en dev que no aparece en prod (datos separados) y un secreto que no se descifra bajo la clave de otro entorno (claves separadas), documentadas en docs/isolation-test.md. Cerraste con el reflejo de seguridad de siempre: git status confirma que ningún .env se cuela; el entregable son los compose files, los .env.example y la prueba documentada, jamás un secreto.
Con esto cumpliste la capacidad de salida del módulo: levantas entornos aislados con su propia clave y sus propias credenciales, y demuestras que un cambio en uno no toca a los demás. Eso es exactamente lo que las ofertas piden cuando escriben "experience with staging and production environments", y ahora lo tienes montado, verificado y documentado.
El Módulo 5 entra a esos entornos que dejaste corriendo y responde la pregunta que sigue: ya tienes un dev aislado donde equivocarte sin miedo, pero ¿cómo pruebas ahí de verdad, y a costo cero? Vas a aprender a generar datos sintéticos, a usar cuentas de prueba y llaves sandbox, a hacer dry runs que no disparan efectos reales, a fijar datos y repetir ejecuciones, y a probar los workflows con nodos AI Agent usando los modelos locales de Ollama que trae el Starter Kit, sin gastar un peso. El cuarto acolchonado ya está construido; el Módulo 5 te enseña a usarlo a fondo.
Recursos
- Docker Compose CLI reference — Docker Docs — la referencia de
docker compose up,down,ls,-p,-fy--env-fileque usas en este proyecto. - Docker Compose — project name — Docker Docs — cómo
-p/COMPOSE_PROJECT_NAMEaísla cada stack; el mecanismo que hace posible la Prueba A. - Set a custom encryption key — n8n Docs — la base de la Prueba B: por qué una clave distinta por entorno impide que un secreto cruce.
- Export and import workflows — n8n Docs — cómo importar el mismo
order-triage.jsonen cada entorno. - Self-hosted AI Starter Kit — GitHub — la base reproducible y los modelos locales de Ollama que el Módulo 5 va a explotar para probar a costo cero.