Apps multi-servicio (estilo compose)

Una app puede correr varios contenedores — ej. un frontend web más un cache o una base de datos internos. Es la versión de la plataforma de un stack de Compose: 1 app = N servicios, pero cuenta como una unidad en tu cuota.

¿Ya tenés un docker-compose.yml? Importalo

En el tab Multi-service del form de deploy, poné tu repo Git (y un token si es privado) y hacé click en Import from docker-compose.yml. La plataforma lee el compose y te rellena los servicios solos — builds/imágenes, puertos, environment, montajes de volúmenes nombrados, y cuál servicio es público. Revisás las filas y desplegás.

Credenciales generadas (seguro por defecto)

Las variables que parecen credenciales — nombres con PASS / PASSWORD / SECRET / TOKEN / KEY — reciben un valor fuerte generado, aunque tu compose tenga un default (esos defaults, como root o dev-token, son inseguros). El mismo ${VAR} mantiene un único valor entre servicios, así por ejemplo el password de la DB coincide entre tu db y tu backend.

Tras importar, el form muestra un recuadro 🔐 Generated credentials con un botón Download .env — se muestran una sola vez, así que guardalas. (Las credenciales de la DB también las recuperás después con Manage database.) Las vars no-secretas mantienen su default del compose.

Lo que no puede traer (y avisa)

Los bind mounts de host (./codigo:/app), porque la plataforma hornea el código en la imagen, y depends_on / healthcheck. El bind de un init-script de DB (schema.sql) también se descarta — cargá tu esquema una vez con Manage database. (Repos de GitHub.)

Reglas

  • Pasás un array services (al menos uno).
  • Al menos uno debe ser public: true. Podés exponer varios (ej. un SPA y su API): el primer público toma https://<app>.cynchro.cloud, los extra reciben https://<app>-<servicio>.cynchro.cloud (override por servicio con domain).
  • Los servicios internos (public:false) no se exponen a internet. Viven en una red privada por-app y son alcanzables por los otros servicios por su name (ej. http://cache:6379). Las vars de discovery (<SVC>_HOST, <SVC>_URL, …) se inyectan solas — ver el Despliegue full-stack.
  • Cada servicio puede usar su propia image, o buildear desde un repo compartido a nivel app.
  • repo / branch / repoToken quedan a nivel app (un repo para el stack); port / env / cpu / memory / secrets son por servicio.

Ejemplo — web público + redis interno

curl -s -X POST $API/deploy \
  -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{
    "name": "shop",
    "services": [
      { "name": "web",   "image": "acme/storefront:1.0", "port": 8080, "public": true,
        "env": { "REDIS_URL": "redis://cache:6379" } },
      { "name": "cache", "image": "redis:7-alpine",      "port": 6379, "public": false }
    ]
  }'
  • https://shop.cynchro.cloud → el contenedor web.
  • web alcanza a cache por la red privada en redis://cache:6379.
  • cache es invisible desde internet.

Campos de cada servicio

Campo Requerido Notas
name DNS-safe; también es el hostname interno.
image ✅* Imagen prearmada, O omitir para buildear desde el repo a nivel app.
dockerfile / context   Cuando buildea desde repo (rutas relativas).
port   Default 80. Puerto donde escucha el servicio.
public   true para exponerlo (podés exponer más de uno).
domain   Override del hostname de este servicio público.
cpu / memory   Límites por servicio.
env   Env por servicio (cifrado at-rest).
envFrom / secretFiles   Referencias a Secrets por servicio.
buildArgs   Claves de env a hornear en build (--build-arg), ej. ["VITE_API_URL"].
envFilePath   Renderiza el env de este servicio como un .env read-only en esta ruta, ej. /var/www/html/.env.
volumes   Rutas de montaje persistentes (absolutas), ej. ["/var/lib/mysql"] — para bases de datos.

* Cada servicio necesita su propia image o un repo a nivel app para buildear.

Bases de datos (volúmenes persistentes)

La plataforma no lee el docker-compose.yml de tu repo — los servicios los declarás acá. Una base de datos necesita una ruta en volumes para que sus datos sobrevivan a los redeploys.

Agregá la base como un servicio interno con un montaje en volumes. Los otros servicios la alcanzan por nombre (ej. db). Los datos en un volumen sobreviven redeploys/restarts; se borran solo cuando borrás la app.

-d '{
  "name": "myapp",
  "services": [
    { "name":"web",     "image":"myorg/web:1.0", "port":80, "public":true,
      "env": { "DB_HOST":"db", "DB_USER":"root", "DB_PASS":"secret", "DB_NAME":"app" } },
    { "name":"db",      "image":"mysql:8", "port":3306, "public":false,
      "env": { "MYSQL_ROOT_PASSWORD":"secret", "MYSQL_DATABASE":"app" },
      "volumes": ["/var/lib/mysql"] }
  ]
}'

Cargar un esquema: las imágenes oficiales mysql/postgres corren los scripts de /docker-entrypoint-initdb.d solo en un volumen nuevo y vacío. Dos opciones fáciles:

  • Una vez, a mano: abrí el gestor de base de datos (botón Database), conectate a db, y corré tu schema.sql.
  • Horneado en la imagen: agregá un db/Dockerfile chico a tu repo (FROM mysql:8 + COPY schema.sql /docker-entrypoint-initdb.d/01-schema.sql) y buildeá ese servicio desde el repo. El esquema se carga solo en el primer init.

Buildear todos los servicios desde un repo

-d '{
  "name": "stack",
  "repo": "https://github.com/acme/monorepo",
  "services": [
    { "name":"api", "dockerfile":"api/Dockerfile",  "context":"api",  "port":3000, "public":true },
    { "name":"worker", "dockerfile":"worker/Dockerfile", "context":"worker", "port":0, "public":false }
  ]
}'

El repo se clona una vez y cada servicio se buildea desde su propio Dockerfile.

Ciclo de vida

Restart / stop / delete actúan sobre toda la app (todos sus contenedores). Borrarla también destruye la red privada. El self-healing vigila cada contenedor — si alguno cae, se redesplega la app.

→ Volver a lo básico: Desplegar desde Git · Inyectar config: Secrets


This site uses Just the Docs, a documentation theme for Jekyll.