Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Docker y contenedores

Ejecuta ZeroClaw en Docker, Podman, Kubernetes o cualquier entorno compatible con OCI.

Imágenes oficiales

Publicado en GitHub Container Registry (ghcr.io) en cada versión estable:

  • ghcr.io/zeroclaw-labs/zeroclaw:latest: última versión estable
  • ghcr.io/zeroclaw-labs/zeroclaw:v0.7.5: fijado
  • ghcr.io/zeroclaw-labs/zeroclaw:debian: imagen basada en Debian (más grande, mayor compatibilidad con glibc)

Multi-arch: linux/amd64, linux/arm64.

Nota sobre el acceso al shell: La imagen latest predeterminada es intencionadamente distroless y no incluye sh, ash ni bash. Usa la etiqueta debian si necesitas un shell dentro del contenedor (por ejemplo, para ejecutar docker exec con fines de depuración).

Imagen de Alpine (compilación local)

Dockerfile.alpine genera una imagen de Alpine opcional con binarios de musl enlazados estáticamente para linux/amd64 y linux/arm64. No se publica en ghcr.io.

Para la plataforma local:

sh

docker build -f Dockerfile.alpine -t zeroclaw:alpine .

Para una imagen de registro multiplataforma, cree un builder una vez y suba el manifiesto. Si ya tiene seleccionado un builder de buildx, omita el primer comando:

sh

docker buildx create --use --name zeroclaw-multiarch
docker buildx build -f Dockerfile.alpine \
  --platform linux/amd64,linux/arm64 \
  -t registry.example.com/zeroclaw:alpine \
  --push .

El ejemplo de Compose incluido construye la imagen para la plataforma actual:

sh

docker compose -f docker-compose.yml -f docker-compose.alpine.yml up --build

La imagen de Alpine utiliza el mismo montaje /zeroclaw-data, las mismas variables de entorno de schema-mirror, la misma ruta del panel y el mismo puerto de la puerta de enlace que las imágenes existentes.

Ejecución mínima

sh

docker run -d \
  --name zeroclaw \
  -v zeroclaw-data:/zeroclaw-data \
  -p 42617:42617 \
  ghcr.io/zeroclaw-labs/zeroclaw:latest

La imagen oficial ya enlaza [::] y tiene allow_public_bind = true y require_pairing = false incorporados en su configuración predeterminada, por lo que este ejemplo directo de docker run es accesible de forma predeterminada. Los ejemplos de Compose que aparecen a continuación siguen fijando ambas opciones de enlace de la puerta de enlace para que las configuraciones persistentes o personalizadas no puedan restaurar silenciosamente un escuchador limitado a la interfaz de bucle invertido.

La imagen espera estado persistente en /zeroclaw-data. En la primera ejecución, inicializa una configuración predeterminada: aún necesitas ejecutar quickstart antes de que sea útil:

sh

docker exec -it zeroclaw zeroclaw quickstart

Ejecutar zerocode (la TUI)

La imagen incluye la interfaz de terminal zerocode junto con el binario zeroclaw. El punto de entrada predeterminado es zeroclaw, así que inicia zerocode anulándolo con --entrypoint zerocode y una TTY interactiva (-it). Ambas variantes de imagen publicadas lo incluyen:

distroless (:latest)

docker run -it --entrypoint zerocode ghcr.io/zeroclaw-labs/zeroclaw:latest

debian

docker run -it --entrypoint zerocode ghcr.io/zeroclaw-labs/zeroclaw:debian

zerocode se conecta a un daemon de ZeroClaw en ejecución, así que apúntalo a uno:

  • Daemon del mismo contenedor: ejecútelo contra el contenedor que ya ejecuta el daemon (docker exec -it zeroclaw zerocode), el cual se comunica con el daemon a través del socket IPC local.
  • Un daemon remoto: conéctese a través de WebSocket Secure con zerocode --connect wss://<host>:<port>; consulte Configuración remota (WSS). Esta es la forma portátil de controlar un daemon en contenedor o remoto desde su propia terminal.

Persiste /zeroclaw-data (como en Ejecución mínima) para que la configuración y la identidad que zerocode lee sean las mismas que usa el daemon.

Componer

Un docker-compose.yml mínimo:

servicios:
  zeroclaw:
    imagen: ghcr.io/zeroclaw-labs/zeroclaw:latest
    reiniciar: a menos que se detenga
    puertos:
      - "127.0.0.1:42617:42617"      # puerta de enlace, solo bucle invertido del host
    volúmenes:
      - ./data:/zeroclaw-data
    entorno:
      # host selecciona la interfaz del contenedor; allow_public_bind reconoce el listener que no es de loopback y silencia la advertencia de inicio.
      - ZEROCLAW_gateway__host=0.0.0.0
      - ZEROCLAW_gateway__allow_public_bind=true

Después de que el contenedor se inicie, ejecuta quickstart:

sh

docker compose exec zeroclaw zeroclaw quickstart

Compose debe establecer explícitamente ZEROCLAW_gateway__host y ZEROCLAW_gateway__allow_public_bind. Publicar un puerto no hace accesible una puerta de enlace enlazada a 127.0.0.1 dentro del contenedor, y allow_public_bind = true permite un enlace público sin seleccionar uno. Mantener las dos sobrescrituras juntas también hace que un volumen existente o una configuración personalizada con valores predeterminados de localhost se comporten de forma coherente.

Esto cambia la exposición, por lo que los ejemplos se publican en la interfaz de bucle invertido del host. Hay dos límites independientes implicados y solo uno de ellos se aplica:

  • gateway.host = 0.0.0.0 selecciona la interfaz del contenedor. El tráfico del puente de Docker no llega a través de la interfaz de loopback del contenedor, por lo que debe mantenerse en 0.0.0.0 para que un puerto publicado pueda llegar siquiera a la puerta de enlace.
  • ZEROCLAW_gateway__allow_public_bind es una confirmación, no un control de acceso. Cuando es false, la puerta de enlace registra una advertencia durante el inicio y se enlaza de todos modos; establecerla en true solo silencia esa advertencia. No dependas de ella para mantener privado un listener.
  • La asignación ports: de Compose es el límite que Docker aplica realmente. "127.0.0.1:42617:42617" publica solo en el host del contenedor; "42617:42617" publica en todas las interfaces del host configuradas para Docker.

El límite de autenticación es ZEROCLAW_gateway__require_pairing, cuyo valor predeterminado es true en el esquema, pero es false en la configuración integrada en la imagen. Con el emparejamiento deshabilitado, la puerta de enlace responde a solicitudes no autenticadas en /webhook, /api/config, /api/memory, /api/browse y los puntos de conexión de sesión. Para atender a otros hosts, elimina el prefijo 127.0.0.1: y habilita el emparejamiento o coloca la puerta de enlace detrás de un proxy inverso o túnel que realice la autenticación.

Compose sin root con la imagen de Debian

Para implementaciones de Docker o Podman Compose sin root que necesiten herramientas de shell dentro del contenedor, usa la imagen actual de Debian y vincula un directorio de datos del host:

servicios:
  zeroclaw:
    imagen: ghcr.io/zeroclaw-labs/zeroclaw:debian
    container_name: zeroclaw
    reiniciar: a menos que se detenga
    puertos:
      - "127.0.0.1:42617:42617"
    volúmenes:
      - ./data:/zeroclaw-data
    entorno:
      - ZEROCLAW_gateway__host=0.0.0.0
      - ZEROCLAW_gateway__allow_public_bind=true
    healthcheck:
      prueba: [CMD, zeroclaw, "estado", --format=exit-code]
      interval: 60s
      timeout: 10s
      reintentos: 3
      start_period: 10s

La imagen Debian actual incluye el panel empaquetado fuera de /zeroclaw-data, por lo que el montaje de enlace no lo oculta y no se necesita ninguna anulación de gateway.web_dist_dir. Las anulaciones de la puerta de enlace usan las grafías del espejo del esquema mostradas por ZEROCLAW_gateway__host y ZEROCLAW_gateway__allow_public_bind. Tienen precedencia sobre una configuración persistida con localhost como valor predeterminado, y la asignación ports: restringida al bucle invertido sigue siendo el límite que restringe el alcance desde el host.

macOS: OrbStack vs Colima

macOS no tiene un kernel de Linux nativo, por lo que cada opción (Docker Desktop, Podman, OrbStack, Colima) ejecuta el contenedor dentro de una VM ligera de Linux. Para una máquina de desarrollo Mac, las dos VMs nativas de Mac que vale la pena comparar son OrbStack y Colima; ambas ejecutan el contenedor con los mismos comandos docker run/Compose mencionados anteriormente.

OrbStackColima
MotorVM Linux personalizada y optimizada (optimizada para Apple Silicon)Lima VM + containerd/Docker
Licenciacomercial, freemium (uso personal gratuito)MIT (Lima, que está por debajo, es Apache 2.0)
InterfazAplicación GUI + CLIPrimero CLI (colima start/stop), programable mediante scripts
Mejor cuandoUX pulida, sin complicacionestodo OSS, configuración en código

OrbStack

# Proporciona la CLI de docker:
brew install --cask orbstack

Colima

# la CLI de docker se comunica con la VM de colima:
brew install colima docker docker-compose   # docker-compose = el plugin Compose v2; instálalo si necesitas `docker compose`
colima start --cpu 4 --memory 8   # añadir --network-address para exponer la IP de la VM a macOS

El rendimiento es comparable para cargas de trabajo de desarrollo típicas; los verdaderos diferenciadores son la licencia (comercial vs OSS) y la preferencia de UX, no la velocidad bruta; haz benchmarks de ambos en tu propia máquina si la RAM en reposo o el rendimiento de compilación te importan. En cualquier caso, controlas el motor dentro de la VM con docker; los quadlets de systemd (más abajo) son una característica de hosts Linux y no aplican en macOS.

Podman y quadlets de systemd

En un servidor Linux, la forma más limpia de ejecutar el contenedor a largo plazo es un quadlet de Podman: un archivo de unidad declarativo que systemd convierte en un servicio real. Obtienes el ciclo de vida de systemctl, los registros de journald, el reinicio automático y el ordenamiento de arranque sin daemon y sin el truco de --restart, y el archivo de unidad es configuración que confirmas en git. Este es el patrón recomendado para servidores; docker run/Compose están bien para una laptop.

Un quadlet es un archivo *.container (hermanos: .pod, .volume, .network, .kube, .build, .image). El generador de systemd de Podman lo lee en cada daemon-reload y escribe un .service transitorio; nunca debes crear el .service manualmente.

Las unidades rootful residen en /etc/containers/systemd/; las rootless en ~/.config/containers/systemd/.

/etc/containers/systemd/zeroclaw.container:

[Unit]
Description=ZeroClaw agent runtime
After=network-online.target
Wants=network-online.target

[Container]
# Pin a release in production; :latest is distroless (no shell — use :debian to exec a shell).
Image=ghcr.io/zeroclaw-labs/zeroclaw:latest
ContainerName=zeroclaw
PublishPort=127.0.0.1:42617:42617
Volume=zeroclaw-data:/zeroclaw-data
# Published on host loopback only; drop the 127.0.0.1: prefix to serve other
# hosts, and enable pairing or a tunnel before you do. If you mount a
# localhost-default config, override both gateway.host and
# gateway.allow_public_bind together.
# Optional rolling-upgrade path — re-pull a newer image on (re)start and opt into `podman auto-update`:
Pull=newer
AutoUpdate=registry

[Service]
Restart=always

[Install]
WantedBy=multi-user.target default.target

Desplegar (idempotente, seguro de volver a ejecutar; volver a aplicarlo hace converger el contenedor en ejecución, nunca lo duplica):

sh

sudo cp zeroclaw.container /etc/containers/systemd/
sudo systemctl daemon-reload      # el generador convierte .container en zeroclaw.service
sudo systemctl restart zeroclaw

Luego incorpórelo una vez y adminístrelo como cualquier servicio:

sh

sudo podman exec -it zeroclaw zeroclaw quickstart
systemctl status zeroclaw
journalctl -u zeroclaw -f

No hay un paso de systemctl enable para las unidades generadas: la línea [Install] WantedBy= es lo que hace que se active al arrancar.

  • Fijación de versión vs :latest. Fije una etiqueta o digest (Image=ghcr.io/zeroclaw-labs/zeroclaw:v0.7.5 o ...@sha256:...) para despliegues reproducibles y auditables; la actualización se convierte entonces en un cambio de etiqueta revisable en el archivo .container confirmado. Pull=newer + AutoUpdate=registry proporcionan en cambio actualizaciones continuas, impulsadas por podman-auto-update.timer (sudo systemctl enable --now podman-auto-update.timer). Elija reproducibilidad o actualidad; el bucle de despliegue es el mismo en ambos casos.
  • Variante rootless. Coloca el archivo en ~/.config/containers/systemd/, usa systemctl --user daemon-reload && systemctl --user restart zeroclaw y ejecuta loginctl enable-linger $USER para que sobreviva al cierre de sesión (misma nota sobre lingering que en Service & daemon).
  • WSL2. Las versiones modernas de WSL2 ejecutan systemd ([boot] systemd=true en /etc/wsl.conf, luego wsl --shutdown), por lo que este mismo patrón de quadlet funciona dentro de una distribución de WSL: no hay un dialecto específico de Windows.

Configuración dentro de contenedores

La imagen espera la configuración en /zeroclaw-data/.zeroclaw/. Monta tu configuración local en:

sh

docker run -d --name zeroclaw \
  -v $(pwd)/my-config.toml:/zeroclaw-data/.zeroclaw/config.toml:ro \
  -v zeroclaw-state:/zeroclaw-data/workspace \
  -p 42617:42617 \
  ghcr.io/zeroclaw-labs/zeroclaw:latest

Para cargas de trabajo en contenedores, establezca uri en cada providers.models.<type>.<alias> con una dirección accesible desde el contenedor (p. ej., http://host.docker.internal:11434 para un servidor Ollama en el host de Docker Desktop). El mecanismo genérico de sobrescritura por variables de entorno puede establecer el mismo campo en tiempo de ejecución sin editar la configuración:

sh

ZEROCLAW_providers__models__ollama__home__uri=http://ollama:11434 zeroclaw agent -a assistant

Consulte Proveedores → Anulaciones compatibles con contenedores para conocer la gramática.

Los canales que usan sondeo (Telegram, correo electrónico): funcionan sin más

Los canales iniciados de salida no necesitan ninguna configuración especial del contenedor. Polling de Telegram, IMAP, MQTT, relays de Nostr: todos hacen pull; el contenedor solo necesita egreso.

Canales que reciben webhooks: necesitan ingress

Discord, Slack, GitHub y la mayoría de los canales de webhook necesitan HTTP entrante. Dos opciones:

  1. Exponga la puerta de enlace: -p 42617:42617 + proxy inverso con TLS delante, apunte la URL del webhook a la dirección pública
  2. Usa un túnel: ngrok, Cloudflare Tunnel o Tailscale Funnel; configura la URL del túnel como destino del webhook

Configure un túnel estableciendo el tunnel_provider de nivel superior [tunnel] (variable de entorno de anulación: ZEROCLAW_tunnel__tunnel_provider) a uno de los proveedores compatibles y completando el bloque tunnel.* correspondiente; la lista completa de proveedores y los campos de cada proveedor están en la referencia de configuración. La URL pública resultante es a la que debe apuntar sus emisores de webhooks.

Kubernetes

En el directorio deploy-k8s/ se proporcionan manifiestos de Kubernetes de ejemplo. Fragmento de manifiesto típico:

apiVersion: apps/v1
tipo: Despliegue
metadatos:
  nombre: zeroclaw
especificación:
  réplicas: 1
  estrategia:
    tipo: Recreate         # ZeroClaw es de instancia única por espacio de trabajo
  template:
    especificación:
      contenedores:
        - nombre: zeroclaw
          imagen: ghcr.io/zeroclaw-labs/zeroclaw:v0.7.5
          puertos:
            - `containerPort`: 42617
          volumeMounts:
            - nombre: datos
              ruta de montaje: /zeroclaw-data
          # `containerPort` no publica en el host; un Service o Ingress
          # controla la exposición aquí. Si montas una configuración predeterminada para localhost,
          # sobrescribe gateway.host y gateway.allow_public_bind conjuntamente.
      volumes:
        - nombre: datos
          persistentVolumeClaim:
            nombre de la reclamación: zeroclaw-data

Escalado: ZeroClaw es de escritor único por espacio de trabajo. No escale horizontalmente; ejecute una instancia por agente.

Reautenticación después de cerrar sesión

Si cierras sesión en la interfaz web mientras se ejecuta en un contenedor, el paircode existente deja de ser válido. Genera uno nuevo para volver a iniciar sesión:

sh

docker exec -it zeroclaw zeroclaw gateway get-paircode --new

Para implementaciones con Compose, use docker compose exec en su lugar:

sh

docker compose exec zeroclaw zeroclaw gateway get-paircode --new

Cosas a tener en cuenta

  • Peculiaridades del hostname en macOS (Docker Desktop, colima, Rancher Desktop). host.docker.internal funciona de forma predeterminada en Docker Desktop para macOS. En colima, solo es accesible si lo instalaste con colima start --network-address (de lo contrario, el contenedor no puede ver el host en absoluto; conéctate a través de la IP de la puerta de enlace de la VM, normalmente 192.168.5.2, o haz un túnel a través de una red compartida). Rancher Desktop se comporta como Docker Desktop en versiones recientes, pero ha tenido fallos de resolución de host.docker.internal en versiones anteriores. Si las llamadas al proveedor fallan con connection refused hacia host.docker.internal, verifica con docker run --rm alpine getent hosts host.docker.internal: una salida vacía significa que el hostname no se puede resolver y necesitas una IP explícita.
  • Servicios del lado del host. Si un proveedor es Ollama en el host, uri = "http://host.docker.internal:11434" (bajo [providers.models.ollama.<alias>]) funciona en Docker Desktop. En Linux Docker, es posible que necesites --add-host=host.docker.internal:host-gateway.
  • Persistencia de memoria. La memoria del agente (la base de datos SQLite brain.db) se encuentra en el directorio de configuración en /zeroclaw-data/.zeroclaw/agents/<alias>/workspace/memory/, con las bases de datos de instancia compartidas en /zeroclaw-data/data/. Montar /zeroclaw-data persiste todo; si se omite el volumen, cada reinicio pierde el historial de conversaciones.
  • Montaje bind de /zeroclaw-data. Un montaje bind del host en /zeroclaw-data reemplaza todo el directorio de la imagen, incluida la configuración predeterminada y (anteriormente) el paquete del dashboard. El dashboard ahora se instala en /usr/share/zeroclawlabs/web/dist, fuera del montaje, por lo que un montaje bind ya no lo oculta. En la primera ejecución, monte un directorio vacío del host y el contenedor inicializa una configuración nueva; el gateway detecta automáticamente el dashboard desde su ruta en la imagen.
  • No hay passthrough de hardware por defecto. GPIO / USB requieren banderas --device explícitas (--device /dev/ttyUSB0), y el usuario del contenedor necesita un GID coincidente para los grupos dialout/gpio.

Siguiente