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.
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:
| Bloque | Función |
|---|---|
meta | nombre, versión, tags |
task | imagen, comando, env, recursos |
count | cuántas réplicas |
health | cómo saber si está vivo |
ingress | dominio 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 conheroctl 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:
| Status | Significa |
|---|---|
pending | esperando recursos en el nodo |
running + starting | el contenedor subió, el health check aún no pasó |
running + healthy | recibiendo tráfico |
failed | crasheó. 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:
- Retorne
200 OKsolo cuando la app esté lista para recibir tráfico. - Valide dependencias reales (base, cache, cola).
- 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
/healthzque 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ámetro | Valor | Qué hace |
|---|---|---|
max_parallel | 1 | cuántas réplicas actualizar al mismo tiempo |
min_healthy_time | 10s | cuánto tiempo la nueva debe quedar saludable |
healthy_deadline | 300s | cuánto esperar antes de considerar falla |
auto_revert | true | vuelve 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:
- Edita
app.yamlcambiando el tag de la imagen (1.4.2→1.4.3). - 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-vendaspara ver todas las versiones y quién submiteó cada una.
Próximos pasos
- ¿Quieres entender cuándo rolling no alcanza? Mira Rolling, canary, blue-green y rainbow.
- Para todos los comandos disponibles: Referencia del CLI.