---
title: Implementar en Kubernetes
url: https://platform.claude.com/docs/es/managed-agents/self-hosted/kubernetes
description: Instala el servidor de Claude Managed Agents autoalojado y sus sandboxes en un clúster de Kubernetes con los dos charts de Helm del archivo de la versión y, después, ejecuta una primera sesión en un pod de sandbox.
---

<Note>
  Para solicitar acceso a Claude Managed Agents autoalojado, ponte en contacto con el equipo de tu cuenta de Anthropic.
</Note>

Esta página lleva un clúster vacío de Google Kubernetes Engine (GKE) hasta una sesión que ejecuta una herramienta en un pod de sandbox. Usa los dos charts de Helm del archivo de la versión, que ejecutan todo el stack en el clúster:

* **`cma-server`:** Aplica el esquema de la base de datos y ejecuta dos servidores detrás de un Service, en el namespace `cma-server`.
* **`cma-sandboxes`:** Ejecuta un dispatcher y los pods de sandbox en el namespace `cma`. El dispatcher sondea el servidor en busca de trabajo y asigna a cada sesión su propio pod de sandbox, donde un worker ejecuta las herramientas de shell y de archivos del agente.

Tu base de datos PostgreSQL queda fuera de ambos. En otro tipo de clúster, los pasos son los mismos, pero los requisitos previos del clúster y algunos valores de los charts cambian. Consulta [Otros clústeres](https://platform.claude.com/docs/es/managed-agents/self-hosted/kubernetes#other-clusters).

## Requisitos previos

* **Un clúster:** Kubernetes 1.30 o posterior, con:

  * El controlador Agent Sandbox (en GKE, el [complemento Agent Sandbox](https://docs.cloud.google.com/kubernetes-engine/docs/how-to/how-install-agent-sandbox))
  * Nodos que ejecuten gVisor (en GKE, un [grupo de nodos de GKE Sandbox](https://docs.cloud.google.com/kubernetes-engine/docs/how-to/sandbox-pods))
  * Un plugin de red que aplique NetworkPolicy (en GKE, Dataplane V2 o Calico)

* **Una base de datos:** PostgreSQL 16 o posterior, accesible desde el clúster.

* **Un registro:** Uno del que el clúster pueda descargar imágenes. Tú mismo compilas las imágenes.

* **Una clave de API de Claude:** El servidor la usa para las llamadas al modelo. Para usar otro proveedor, consulta [Proveedor de inferencia](https://platform.claude.com/docs/es/managed-agents/self-hosted/inference-provider).

* **Permisos:** Derechos de cluster-admin o equivalentes. La instalación crea namespaces, Roles y un ClusterRoleBinding.

* **Herramientas en tu máquina:** `helm`, `kubectl`, `docker buildx`, `openssl` y la [CLI `ant`](https://platform.claude.com/docs/es/cli-sdks-libraries/cli/quickstart#installation).

* **El archivo de la versión:** Para solicitar acceso a la descarga, comunícate con el equipo de tu cuenta de Anthropic. Extrae el archivo. Ejecuta todos los comandos de esta página desde su directorio raíz.

## Instalar el stack

<Steps>
  <Step title="Compilar y subir las imágenes">
    Establece estas variables con tus propios valores. `PLATFORM` es la arquitectura de tus nodos. `TAG` es el número de versión, que está en el nombre del archivo de la versión, después de la `v`. Ambos charts esperan imágenes que tengan esta versión como etiqueta.

    ```bash
    REGISTRY=us-docker.pkg.dev/my-project/cma
    TAG=1.2.3
    PLATFORM=linux/amd64
    ```

    Luego compila y sube las tres imágenes:

    ```bash
    docker buildx build --platform "$PLATFORM" -f kubernetes/server/Dockerfile -t "$REGISTRY/cma-server-image:$TAG" --push .
    docker buildx build --platform "$PLATFORM" -f kubernetes/dispatcher/Dockerfile -t "$REGISTRY/cma-dispatcher:$TAG" --push .
    docker buildx build --platform "$PLATFORM" -t "$REGISTRY/cma-sandbox:$TAG" --push kubernetes/sandbox
    ```
  </Step>

  <Step title="Crear un archivo de secretos y la base de datos">
    Crea un archivo con una contraseña de base de datos generada y un marcador de posición para tu clave de API de Claude:

    ```bash
    cat > secrets.env <<EOF
    PG_PASSWORD=$(openssl rand -hex 32)
    UPSTREAM_KEY=sk-ant-...
    EOF
    chmod 600 secrets.env
    ```

    Abre `secrets.env` en un editor y reemplaza `sk-ant-...` con tu clave de API de Claude. Luego conéctate a tu servidor de base de datos como administrador, por ejemplo con `psql`. Ejecuta este SQL, con el valor de `PG_PASSWORD` en lugar de `...`:

    ```sql
    CREATE ROLE cma_server LOGIN PASSWORD '...';
    GRANT cma_server TO CURRENT_USER;
    CREATE DATABASE cma_server OWNER cma_server;
    ```
  </Step>

  <Step title="Crear el namespace y los Secrets del servidor">
    Crea el namespace del servidor y sus Secrets:

    ```bash
    source secrets.env
    ENCRYPTION_KEY=$(openssl rand -base64 32)
    kubectl create namespace cma-server
    kubectl label namespace cma-server pod-security.kubernetes.io/enforce=restricted pod-security.kubernetes.io/enforce-version=latest
    kubectl -n cma-server create secret generic cma-server-db \
      --from-literal=PGPASSWORD="$PG_PASSWORD"
    kubectl -n cma-server create secret generic cma-server-keys \
      --from-literal=CMA_SERVER__API_KEY="$(openssl rand -hex 32)" \
      --from-literal=CMA_STORE__CREDENTIALS_ENCRYPTION_KEY="$ENCRYPTION_KEY"
    kubectl -n cma-server create secret generic cma-server-upstream \
      --from-literal=ANTHROPIC_API_KEY="$UPSTREAM_KEY"
    ```

    Imprime la clave de cifrado con `echo "$ENCRYPTION_KEY"` y guarda una copia fuera del clúster. El servidor usa esta clave para cifrar las credenciales que almacena, y una base de datos restaurada la necesita. Luego elimina `secrets.env`. El Secret `cma-server-db` conserva la contraseña de la base de datos.
  </Step>

  <Step title="Crear el namespace y el certificado de los sandboxes">
    Crea el namespace de los sandboxes y un certificado que el dispatcher sirve y en el que confían los sandboxes:

    ```bash
    kubectl create namespace cma
    kubectl label namespace cma pod-security.kubernetes.io/enforce=restricted pod-security.kubernetes.io/enforce-version=latest
    openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -days 825 \
      -subj "/CN=cma-dispatcher.cma.svc" \
      -addext "subjectAltName=DNS:cma-dispatcher.cma.svc,DNS:cma-dispatcher.cma.svc.cluster.local" \
      -keyout tls.key -out tls.crt
    kubectl -n cma create secret tls cma-dispatcher-tls --cert=tls.crt --key=tls.key --dry-run=client -o yaml | kubectl apply -f -
    kubectl -n cma create configmap cma-dispatcher-ca --from-file=ca.crt=tls.crt --dry-run=client -o yaml | kubectl apply -f -
    ```
  </Step>

  <Step title="Instalar el servidor">
    Guarda esto como `cma-server-values.yaml`, con tu registro y la dirección de tu base de datos:

    ```yaml
    earlierNames: false   # a new install
    image:
      registry: us-docker.pkg.dev/my-project/cma   # REGISTRY in the build commands
    database:
      url: host=10.20.0.5 port=5432 user=cma_server dbname=cma_server sslmode=require   # your database's address
    environments:
      - name: default
        namespace: cma
    ```

    Luego instala el chart:

    ```bash
    helm install cma-server kubernetes/server/chart --namespace cma-server -f cma-server-values.yaml --wait
    kubectl -n cma-server logs job/cma-server-environment-default
    ```

    La instalación crea el entorno `default` y guarda su clave en el Secret `cma-environment` del namespace `cma`. El segundo comando imprime una línea que empieza por `filled Secret cma/cma-environment`. Si un primer `helm install` falla, ejecuta `helm uninstall` para esa release antes de volver a instalar.
  </Step>

  <Step title="Buscar las direcciones del clúster que hay que bloquear">
    Esta instalación permite que los sandboxes accedan a internet pública. El endpoint de la API de un clúster suele tener una dirección pública, así que los sandboxes también podrían acceder a él. Para bloquearlo, enumeras su dirección en `egress.clusterCIDRs` en el paso siguiente.

    Para mostrar la dirección, ejecuta `gcloud container clusters describe my-cluster --format 'value(endpoint)'` con el nombre de tu clúster.

    El propio chart bloquea los rangos privados `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16` y `100.64.0.0/10`. Si tu clúster tiene un rango de pods, servicios o nodos fuera de esos cuatro, enumera también ese rango.
  </Step>

  <Step title="Instalar los sandboxes">
    Guarda esto como `cma-values.yaml`, con tu registro y las direcciones del paso anterior:

    ```yaml
    platform: gke
    image:
      registry: us-docker.pkg.dev/my-project/cma   # REGISTRY in the build commands
    egress:
      mode: publicInternet
      clusterCIDRs:
        - 203.0.113.10/32                          # the cluster's API endpoint
      cmaServer:
        pods:
          namespace: cma-server
          matchLabels:
            anthropic.com/role: server
        port: 8400
    ```

    `egress.cmaServer` abre el camino hacia tus servidores. Para comparar `publicInternet` con los otros dos modos, consulta [A qué pueden acceder los sandboxes](https://platform.claude.com/docs/es/managed-agents/self-hosted/kubernetes#what-sandboxes-can-reach).

    Si tu clúster usa una de estas configuraciones de DNS, agrega una línea al archivo:

    * **Cloud DNS para GKE, que es lo que usan los clústeres Autopilot:** `gke: {dns: cloudDns}`
    * **NodeLocal DNSCache:** `gke: {dns: nodeLocalCache, kubeDnsUpstreamIP: <address>}`. Para mostrar la dirección, ejecuta `kubectl -n kube-system get svc kube-dns-upstream -o jsonpath='{.spec.clusterIP}'`.

    Luego instala el chart:

    ```bash
    helm install cma kubernetes/chart --namespace cma -f cma-values.yaml --wait
    ```
  </Step>
</Steps>

## Ejecutar una sesión

Estos pasos usan los nombres de una instalación nueva. Si tu archivo de valores del servidor tiene `earlierNames: true`, encuentra los nombres que debes usar en su lugar en [Kubernetes: los nombres de tus objetos](https://platform.claude.com/docs/es/managed-agents/self-hosted/upgrade#object-names).

<Steps>
  <Step title="Reenviar el puerto del servidor">
    En una terminal, reenvía el puerto del servidor a tu máquina:

    ```bash
    kubectl -n cma-server port-forward svc/cma-server 8400:8400
    ```
  </Step>

  <Step title="Apuntar la CLI a tu servidor">
    En una segunda terminal, apunta la CLI `ant` al puerto reenviado y lee la clave de API del servidor desde el Secret `cma-server-keys`:

    ```bash
    export ANTHROPIC_BASE_URL=http://127.0.0.1:8400
    export ANTHROPIC_API_KEY=$(kubectl -n cma-server get secret cma-server-keys -o jsonpath='{.data.CMA_SERVER__API_KEY}' | base64 -d)
    ```
  </Step>

  <Step title="Crear un agente y pedirle que ejecute un comando">
    Sigue tres pasos de [Ejecutar tu primera sesión](https://platform.claude.com/docs/es/managed-agents/self-hosted/quickstart#run-your-first-session) de la guía de inicio rápido: Crear un agente, Iniciar una sesión y Pedirle al agente que ejecute un comando. En Iniciar una sesión, reemplaza `compose-sandbox` con `default`, el nombre del entorno que crean los charts.
  </Step>

  <Step title="Encontrar el pod de sandbox">
    El agente responde con el nombre de host del pod que ejecutó el comando. En una tercera terminal, `kubectl -n cma get pods` muestra un pod con ese nombre junto al del dispatcher. De forma predeterminada, el pod desaparece unos 5 minutos después de que termine el turno del agente. Si no aparece en la lista, pídele al agente que vuelva a ejecutar `hostname`.

    Para salir del chat, presiona Ctrl+C.
  </Step>
</Steps>

## Poner TLS delante del servidor

El servidor usa HTTP sin cifrar en el puerto 8400, y las solicitudes de tus aplicaciones llevan la clave de API del servidor. Antes de que se conecten clientes desde fuera del clúster, coloca delante del Service `cma-server` un balanceador de carga o un ingress que termine TLS. Debe:

* Pasar las rutas de las solicitudes sin modificarlas.
* Aceptar cuerpos de solicitud de hasta 32 MiB.
* Pasar las respuestas sin búfer.
* Establecer un tiempo de espera de lectura predeterminado de 2 minutos. Para las solicitudes de Git, establecer un tiempo de espera de lectura de 10 minutos para las rutas que coincidan con `^/cma-[a-z]+/git/`.
* Enviar tráfico solo a servidores que estén listos. El Service `cma-server` ya lo hace. Un balanceador de carga que llega directamente a los pods debe sondear `/cma-server/readyz` en cada pod y omitir el que devuelva `503`.
* Nunca reenviar un `POST` que un servidor ya haya aceptado.
* Nunca almacenar en caché una respuesta.

Para comprobarlo desde fuera del clúster, ejecuta `curl -s https://<host>/cma-server/healthz`. La respuesta es `{"status":"ok"}`.

## A qué pueden acceder los sandboxes

En `cma-values.yaml`, `egress.mode: publicInternet` permite que los sandboxes accedan a internet pública.

Esta tabla lo compara con los otros dos modos:

| `egress.mode`                     | A qué pueden acceder los sandboxes                                                                                                                                                                                                                    |
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `closed`, el valor predeterminado | Tus servidores y el dispatcher, y ningún servidor DNS                                                                                                                                                                                                 |
| `publicInternet`                  | Tus servidores, el dispatcher, un servidor DNS e internet pública                                                                                                                                                                                     |
| `gateway`                         | Tus servidores, el dispatcher y, a través del gateway de salida, los hosts que enumeres. Consulta [Activa el gateway en Kubernetes](https://platform.claude.com/docs/es/managed-agents/self-hosted/agent-identity#turn-on-the-gateway-on-kubernetes). |

## Otros clústeres

Establece `platform` en `cma-values.yaml` en `eks` para Amazon EKS, en `aks` para Azure Kubernetes Service (AKS), o en `generic` para cualquier otro clúster. Los tres necesitan el controlador Agent Sandbox original con sus extensiones y un plugin de red que aplique NetworkPolicy. También necesitan:

* **Amazon EKS:** Un grupo de nodos con `runsc` de gVisor registrado como un handler de containerd llamado `runsc`. Etiqueta los nodos con `cma.anthropic.com/sandbox-node=true` y aplícales el taint `cma.anthropic.com/sandbox-node=true:NoSchedule`. El chart registra la RuntimeClass `gvisor` asociada a ese handler.
* **AKS:** Un grupo de nodos con Pod Sandboxing, con la misma etiqueta y el mismo taint que en Amazon EKS. Pon el nombre del motor de políticas de red del clúster (`cilium`, `calico` o `azure`) en `aks.networkPolicy`. Mantén `egress.mode: publicInternet`, el único modo que el chart acepta en AKS. Prueba el chart en tu clúster antes de que los agentes hagan trabajo real en él.
* **Cualquier otro clúster:** Una RuntimeClass para gVisor u otro entorno de ejecución aislado. Pon su nombre en `runtimeClass.name`.

La carpeta `kubernetes/chart/examples/` del archivo de la versión contiene un archivo de valores completo para cada tipo de clúster.

## Liberar el volumen de una sesión

Con los valores predeterminados del chart, cada sesión tiene un volumen de 10 GiB para `/workspace`. El volumen permanece después de que el pod de la sesión desaparece, así que el siguiente mensaje de la sesión encuentra sus archivos.

| Para liberar                                    | Haz esto                                                                                                                                                                                                                                                                                                            |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| El volumen de una sesión                        | Archiva la sesión después de que termine su turno. El dispatcher suele eliminar el volumen en unos 5 minutos.                                                                                                                                                                                                       |
| Los volúmenes de las sesiones que nadie archiva | Establece `dispatcher.retentionDays` en `cma-values.yaml`. El dispatcher elimina entonces un volumen cuando su sesión ha estado inactiva durante esa cantidad de días, y el siguiente mensaje de la sesión empieza con un `/workspace` vacío. El valor predeterminado del chart, `0`, conserva todos los volúmenes. |

Los comandos de esta sección son para GKE. En otro clúster, usa `sandboxclaims` en lugar de `sandboxes`.

Eliminar una sesión no libera su volumen. Archiva primero la sesión. Elimínala solo después de que `kubectl -n cma get sandboxes -l anthropic.com/session-id=<session-id>` no muestre nada.

Si eliminaste una sesión sin archivarla, libera tú mismo su volumen:

1. Encuentra el ID de la sesión en el registro del dispatcher. Este comando imprime una línea por cada sesión que parece estar eliminada y aún tiene un volumen, con el ID después de `session_id`:

   ```bash
   kubectl -n cma logs deployment/cma-dispatcher --since=6m \
     | grep "could not check the claim's session" | grep "403 Forbidden"
   ```

   El dispatcher escribe estas líneas cada 5 minutos. Si la sesión no aparece en la lista, espera 5 minutos y vuelve a ejecutar el comando.

2. Confirma que la sesión está eliminada. Con el puerto reenviado y `ANTHROPIC_API_KEY` establecida como en [Ejecutar una sesión](https://platform.claude.com/docs/es/managed-agents/self-hosted/kubernetes#run-a-session), ejecuta `ant --base-url http://127.0.0.1:8400 beta:sessions retrieve --session-id <session-id>`. Continúa solo si la respuesta es `404 Not Found` con `Session not found`. Si el comando imprime una sesión, detente: esa sesión no está eliminada.

3. Elimina el objeto de Kubernetes al que pertenece el volumen. Los archivos de la sesión se eliminan con él:

   ```bash
   kubectl -n cma delete sandboxes -l anthropic.com/session-id=<session-id>
   ```

## Renovar claves y certificados

Una clave de entorno es válida durante 365 días, y el certificado del dispatcher es válido durante 825 días. Renueva cada uno antes de que caduque.

En los últimos 30 días de una clave de entorno, el servidor registra una advertencia en cada solicitud que usa la clave. La advertencia empieza por `environment key is within 30 days of expiry`.

### Renovar la clave del entorno que crearon los charts

Para ver cuándo caduca la clave, ejecuta `kubectl -n cma describe secret cma-environment`.

Ejecuta estos dos comandos al menos una vez durante los últimos 60 días de validez de la clave, desde el directorio raíz del archivo de la versión instalada:

```bash
helm upgrade cma-server kubernetes/server/chart --namespace cma-server -f cma-server-values.yaml --wait
kubectl -n cma rollout restart deployment/cma-dispatcher
```

Cada vez que se ejecuta el comando `helm upgrade` del chart del servidor, este guarda una clave nueva en el Secret si la clave caduca en un plazo de 60 días. Nada más renueva la clave. El segundo comando reinicia el dispatcher para que lea la clave nueva.

### Renovar el certificado del dispatcher

Vuelve al paso “Crear el namespace y el certificado de los sandboxes”. Repite su comando `openssl` y los dos comandos `kubectl` que lo siguen. Luego reinicia el dispatcher con `kubectl -n cma rollout restart deployment/cma-dispatcher`.

### Reemplazar una clave de entorno para tus propios workers

Los charts solo renuevan su propia clave. Si creaste una clave tú mismo, como en [Crear un entorno y su clave](https://platform.claude.com/docs/es/managed-agents/self-hosted/call-the-api#create-an-environment-and-its-key), reemplázala con la CLI de administración. La CLI está en el nivel superior del archivo de la versión.

Ejecuta los comandos en la segunda terminal de [Ejecutar una sesión](https://platform.claude.com/docs/es/managed-agents/self-hosted/kubernetes#run-a-session), donde `ANTHROPIC_API_KEY` contiene la clave de API del servidor. Mantén el reenvío de puertos en ejecución en la primera terminal. Si accedes a tu servidor en otra dirección, pasa esa dirección como `-base-url`, y no necesitas reenvío de puertos.

<Steps>
  <Step title="Crear una clave nueva para el mismo entorno">
    En `-env`, pasa el ID del entorno:

    ```bash
    echo "$ANTHROPIC_API_KEY" | ./cma-server-admin mint-key \
      -base-url http://127.0.0.1:8400 -env env_01595EKxaaTTGwwY3kyXdtbs -key-file -
    ```

    El comando imprime la clave nueva, e imprime el ID y la fecha de caducidad de la clave en la salida de error estándar.
  </Step>

  <Step title="Pasar tus workers a la clave nueva">
    Establece la clave nueva como `ANTHROPIC_ENVIRONMENT_KEY` en cada proceso que sondea y reinícialo.
  </Step>

  <Step title="Revocar la clave antigua">
    Un worker que aún usa una clave revocada recibe un error `401` y abandona la sesión que estaba atendiendo. Para encontrar el ID de la clave antigua, ejecuta `list-keys` con las mismas opciones `-base-url`, `-env` y `-key-file`. Cuando ningún worker use ya la clave antigua, revócala por ese ID:

    ```bash
    echo "$ANTHROPIC_API_KEY" | ./cma-server-admin revoke-key \
      -base-url http://127.0.0.1:8400 -env env_01595EKxaaTTGwwY3kyXdtbs \
      -key-id etok_01AbCdEfGhIjKlMnOpQrStUv -key-file -
    ```
  </Step>
</Steps>

## Desinstalar el stack

Elimina primero los sandboxes. `helm uninstall` elimina las políticas de red que confinan a los sandboxes, pero los sandboxes que el dispatcher creó para las sesiones siguen ejecutándose.

Estos pasos no afectan a tu base de datos, pero eliminan los Secrets. Para volver a usar la base de datos, necesitas tu copia de la clave de cifrado.

<Steps>
  <Step title="Detener el dispatcher y eliminar los sandboxes">
    ```bash
    kubectl -n cma scale deployment/cma-dispatcher --replicas=0
    kubectl -n cma delete sandboxwarmpool cma-sandbox
    kubectl -n cma delete sandboxclaims --all
    ```

    En GKE, ejecuta también este comando:

    ```bash
    kubectl -n cma delete sandboxes --all
    ```
  </Step>

  <Step title="Esperar hasta que los sandboxes desaparezcan">
    Repite este comando hasta que los sandboxes, sus pods y sus volúmenes hayan desaparecido:

    ```bash
    kubectl -n cma get sandboxes,pods,pvc
    ```
  </Step>

  <Step title="Desinstalar los charts y eliminar los namespaces">
    ```bash
    helm uninstall cma --namespace cma
    helm uninstall cma-server --namespace cma-server
    kubectl delete namespace cma cma-server
    ```
  </Step>
</Steps>

## Próximos pasos

* **Actualizar:** [Actualizar Claude Managed Agents autoalojado](https://platform.claude.com/docs/es/managed-agents/self-hosted/upgrade) contiene los comandos `helm upgrade`.
* **Cambiar otros valores de los charts:** Consulta los comentarios de `kubernetes/server/chart/values.yaml` para el servidor y de `kubernetes/chart/values.yaml` para los sandboxes.
* **Llamar a la API desde tu código:** Consulta [Llamar a la API en un servidor autoalojado](https://platform.claude.com/docs/es/managed-agents/self-hosted/call-the-api).
* **Trabajar en un navegador:** Consulta [Usa la consola en tu servidor](https://platform.claude.com/docs/es/managed-agents/self-hosted/console).
