Deploy de la primera app

Levanta una aplicación Node.js con base Postgres en 50 líneas de YAML. Incluye health check, rolling update y rollback.

Deploy·10 min·última revisión 2026-04-26

Un deploy en HeroCtl es un archivo YAML enviado al cluster. El cluster decide dónde correr, cuándo actualizar y cómo reaccionar si algo se rompe. Tú solo describes el deseo.

Anatomía del job spec

Un job define un servicio completo: imagen, réplicas, recursos, ingress, secretos. Todo en un archivo solo. Cinco bloques importan:

BloqueFunción
metanombre, versión, tags
taskimagen, comando, env, recursos
countcuántas réplicas
healthcómo saber si está vivo
ingressdominio público y TLS

50 líneas cubren el 90% de los casos.

Ejemplo completo: web Node.js + Postgres

Vamos a levantar una API Node.js con 2 réplicas y un Postgres adyacente. Crea el archivo app.yaml:

job: api-vendas
version: 1

tasks:
  - name: postgres
    image: postgres:16-alpine
    count: 1
    resources:
      cpu: 500
      memory: 512
    env:
      POSTGRES_DB: vendas
      POSTGRES_USER: app
    secrets:
      POSTGRES_PASSWORD: db-password
    volumes:
      - name: pgdata
        path: /var/lib/postgresql/data
        size: 10Gi
    health:
      tcp: 5432
      interval: 10s
      timeout: 3s

  - name: web
    image: minhaempresa/api-vendas:1.4.2
    count: 2
    resources:
      cpu: 250
      memory: 256
    env:
      DATABASE_URL: postgres://app@postgres.local:5432/vendas
      NODE_ENV: production
    secrets:
      DATABASE_PASSWORD: db-password
      JWT_SECRET: jwt-secret
    health:
      http: /healthz
      port: 3000
      interval: 5s
      healthy_after: 2
      unhealthy_after: 3
    ingress:
      host: api.minhaempresa.com
      port: 3000
      tls: true

Cincuenta líneas, una app entera con base persistente, secretos inyectados, health check y dominio con certificado.

Nota: los secretos referenciados (db-password, jwt-secret) deben existir antes. Créalos con heroctl secret create db-password --value '...'. Mira Referencia del CLI para todos los comandos.

Submitir el job

Con el archivo listo, un comando envía el deseo al cluster:

heroctl job submit app.yaml

Salida:

job:       api-vendas
version:   1 (new)
tasks:     2 (postgres, web)
plan:
  + create alloc postgres-a1b2  on node-2
  + create alloc web-c3d4       on node-1
  + create alloc web-e5f6       on node-3
deploy:    rolling
status:    accepted (id: dep-2026-04-26-001)

El cluster planificó dónde correrá cada contenedor e inició la ejecución en segundo plano. El comando vuelve en 1–2 segundos. No espera a que la app suba.

Acompañar el progreso

Para ver lo que está pasando de hecho:

heroctl alloc list --job api-vendas
ALLOC          TASK      NODE     STATUS    HEALTH    AGE
postgres-a1b2  postgres  node-2   running   healthy   12s
web-c3d4       web       node-1   running   starting  8s
web-e5f6       web       node-3   running   healthy   8s

Los estados que importan:

StatusSignifica
pendingesperando recursos en el nodo
running + startingel contenedor subió, el health check aún no pasó
running + healthyrecibiendo tráfico
failedcrasheó. Mira logs.

Logs en tiempo real

Para ver la salida de la app mientras sube:

heroctl logs -f --job api-vendas --task web

La flag -f sigue el stream. Sal con Ctrl+C. Para una alloc específica:

heroctl logs -f --alloc web-c3d4

Los logs se almacenan por 7 días por defecto. Para historial largo, integra con un destino externo (mira Observabilidad).

El health check es obligatorio

HeroCtl no hace deploy sin health check. No es restricción cosmética: sin él, no hay cómo distinguir contenedor subiendo de contenedor roto.

Tu aplicación debe exponer un endpoint que:

  1. Retorne 200 OK solo cuando la app esté lista para recibir tráfico.
  2. Valide dependencias reales (base, cache, cola).
  3. Responda en menos de 1 segundo.

Ejemplo mínimo en Node.js:

app.get('/healthz', async (req, res) => {
  try {
    await db.query('SELECT 1')
    res.status(200).json({ ok: true })
  } catch (err) {
    res.status(503).json({ ok: false, error: err.message })
  }
})

Atención: un /healthz que siempre retorna 200 es peor que no tenerlo. Esconde fallas y el deploy pasa creyendo que todo está bien.

Rolling update por defecto

Sin configuración extra, las actualizaciones son rolling update. El cluster cambia una réplica a la vez, espera a que quede saludable, y solo entonces toca la próxima.

Defaults del rolling update:

ParámetroValorQué hace
max_parallel1cuántas réplicas actualizar al mismo tiempo
min_healthy_time10scuánto tiempo la nueva debe quedar saludable
healthy_deadline300scuánto esperar antes de considerar falla
auto_reverttruevuelve solo si falla

Para customizar, agrega un bloque update en el task:

update:
  max_parallel: 2
  min_healthy_time: 30s
  healthy_deadline: 600s
  auto_revert: true

Más paralelismo = deploy rápido + ventana mayor de riesgo. Más tiempo saludable = deploy lento + confianza mayor.

Actualizar la app

Cambiaste código, hiciste build, push de la imagen con tag nueva. Para promover:

  1. Edita app.yaml cambiando el tag de la imagen (1.4.21.4.3).
  2. Submitea de nuevo:
heroctl job submit app.yaml

El cluster compara las dos versiones, percibe que solo la imagen del web cambió y corre rolling update solo en ese task. Postgres sigue intocado.

Acompaña con:

heroctl deploy status dep-2026-04-26-002
# task: web
# strategy: rolling
# progress: 1/2 (50%)
# state: rolling
# next: web-e5f6 (em 8s)

Rollback

Si algo sale mal y necesitas volver:

heroctl job revert api-vendas --version 1

El comando reaplica el spec de la versión anterior, con el mismo rolling update. En 30–60 segundos estás de vuelta al estado bueno conocido. No hay "rollback de base" — las schema migrations son responsabilidad de la aplicación.

Nota: las versiones antiguas quedan guardadas indefinidamente. Lístalas con heroctl job history api-vendas para ver todas las versiones y quién submiteó cada una.

Próximos pasos

#deploy#primeros-pasos#yaml#rolling