Docker Compose
Compose es la forma más directa de ejecutar el Agent en un solo host (una VM dedicada, por ejemplo). Para orquestadores, consulta Kubernetes o la configuración de producción.
Un compose.yml mínimo
Sección titulada «Un compose.yml mínimo»services: whystack-agent: image: <address>/whystack-agent@sha256:<digest> restart: unless-stopped stop_grace_period: 150s volumes: - whystack-identity:/identity environment: RUNNER_TUNNEL_URL: wss://tunnel.whystack.ai:8443 RUNNER_ENROLL_URL: https://app.whystack.ai RUNNER_ATTESTATION_MODE: bootstrap-token RUNNER_BOOTSTRAP_TOKEN: ${RUNNER_BOOTSTRAP_TOKEN} RUNNER_IDENTITY_DIR: /identity RUNNER_INSTANCE_ID: agent-prod-1 RUNNER_CONNECTORS: real RUNNER_SECRET_STORE_MODE: aws NODE_ENV: production
volumes: whystack-identity:stop_grace_period mayor que RUNNER_DRAIN_TIMEOUT_MS (2 minutos por defecto), o el SIGKILL
llega a mitad del drenaje.
Salud del contenedor
Sección titulada «Salud del contenedor»La imagen declara un HEALTHCHECK de liveness (whystack-health live) sin ningún puerto: el Agent
escribe la señal en un archivo y Docker la lee mediante exec. docker compose ps muestra healthy
cuando el proceso está vivo, lo que no significa que esté conectado. Para saber si está sirviendo:
docker compose exec whystack-agent whystack-health readySi ejecutas el contenedor con read_only: true, monta tmpfs: [/tmp]: ahí es donde se escribe la
señal (RUNNER_HEALTH_DIR).
La identidad necesita un volumen con escritura
Sección titulada «La identidad necesita un volumen con escritura»Es lo que hace que docker compose pull && up -d no cueste un bootstrap token nuevo. Sin él, la
identidad muere con el contenedor y el token anterior ya está gastado: es de un solo uso.
Prefiere un volumen con nombre, como en el ejemplo de arriba. La imagen ya trae /identity con
propietario uid 1000 (se ejecuta como no root), y un volumen con nombre hereda esa propiedad al
crearse, así que funciona sin que tengas que hacer nada.
Un bind mount (./identity:/identity) no hereda nada: ahí la propiedad es la del directorio del
host, y tiene que pertenecer al uid 1000. Nunca lo montes con :ro.
Si el directorio no tiene permiso de escritura, el boot no falla. El Agent avisa y sigue, y solo te enteras en el siguiente deploy:
could not persist identity — next boot will re-enroll from scratchLos certificados, si usas un certificado aprovisionado
Sección titulada «Los certificados, si usas un certificado aprovisionado»volumes: - ./certs:/certs:roEl directorio debe contener runner.crt, runner.key y ca.pem. El entrypoint los lee y los exporta
como RUNNER_CERT_PEM, RUNNER_KEY_PEM y RUNNER_CA_PEM. El Agent lee el PEM inline, no como
ruta, y montar el directorio evita la pesadilla de PEMs multilínea dentro de un env_file.
Ese camino ignora el enrollment y la identidad persistida: los dos bloques de arriba dejan de aplicar, y el certificado no se renueva. Consulta Identidad y enrollment.
Si el Agent corre en EC2 y usa la cadena de credenciales
Sección titulada «Si el Agent corre en EC2 y usa la cadena de credenciales»Con RUNNER_AWS_CRED_MODE=chain, la cadena por defecto del SDK resuelve a través del instance profile
sin ningún volumen extra. Si en cambio usas un perfil local basado en archivo, móntalo:
volumes: - ${HOME}/.aws:/home/node/.aws:roSin ese volumen el contenedor no ve el perfil, y la cadena falla sin decir por qué: el clásico “funciona en mi terminal, pero no en el contenedor”.