Hosting
Fly.io
Objetivo: Gateway de OpenClaw ejecutándose en una máquina de Fly.io con almacenamiento persistente, HTTPS automático y acceso a Discord/canales.
Qué necesitas
- CLI flyctl instalada
- Cuenta de Fly.io (el nivel gratuito es suficiente)
- Autenticación del modelo: clave de API del proveedor de modelos elegido
- Credenciales del canal: token del bot de Discord, token de Telegram, etc.
Ruta rápida para principiantes
- Clona el repositorio y personaliza
fly.toml - Crea la aplicación y el volumen, y configura los secretos
- Despliega con
fly deploy - Accede mediante SSH para crear la configuración o utiliza la interfaz de control
Crear la aplicación de Fly
git clone https://github.com/openclaw/openclaw.gitcd openclaw # elige tu propio nombrefly apps create my-openclaw # normalmente, 1 GB es suficientefly volumes create openclaw_data --size 1 --region iadElige una región cercana. Opciones habituales: lhr (Londres), iad (Virginia), sjc (San José).
Configurar fly.toml
Edita fly.toml para que coincida con el nombre y los requisitos de tu aplicación. El archivo fly.toml versionado en el repositorio es la plantilla pública que se muestra a continuación; deploy/fly.private.toml es la variante reforzada sin IP pública (consulta Despliegue privado).
app = "my-openclaw" # nombre de tu aplicaciónprimary_region = "iad" [build] dockerfile = "Dockerfile" [env] NODE_ENV = "production" OPENCLAW_PREFER_PNPM = "1" OPENCLAW_STATE_DIR = "/data" NODE_OPTIONS = "--max-old-space-size=1536" [processes] app = "node dist/index.js gateway --allow-unconfigured --port 3000 --bind lan" [http_service] internal_port = 3000 force_https = true auto_stop_machines = false auto_start_machines = true min_machines_running = 1 processes = ["app"] [[vm]] size = "shared-cpu-2x" memory = "2048mb" [mounts] source = "openclaw_data" destination = "/data"El punto de entrada de la imagen de Docker de OpenClaw es tini, que ejecuta node openclaw.mjs gateway de forma predeterminada. [processes] de Fly reemplaza el CMD de Docker (aquí ejecuta directamente node dist/index.js gateway ..., el mismo punto de entrada compilado) sin modificar ENTRYPOINT, por lo que el proceso sigue ejecutándose mediante tini.
Configuración clave:
| Ajuste | Motivo |
|---|---|
--bind lan |
Enlaza con 0.0.0.0 para que el proxy de Fly pueda acceder al Gateway |
--allow-unconfigured |
Inicia sin un archivo de configuración (lo crearás después) |
internal_port = 3000 |
Debe coincidir con --port 3000 (o OPENCLAW_GATEWAY_PORT) para las comprobaciones de estado de Fly |
memory = "2048mb" |
512 MB es insuficiente; se recomiendan 2 GB |
OPENCLAW_STATE_DIR = "/data" |
Conserva el estado en el volumen |
Configurar los secretos
# obligatorio: token de autenticación del Gateway para enlaces que no sean local loopbackfly secrets set OPENCLAW_GATEWAY_TOKEN=$(openssl rand -hex 32) # claves de API de los proveedores de modelosfly secrets set ANTHROPIC_API_KEY=example-anthropic-key-not-real # opcional: otros proveedoresfly secrets set OPENAI_API_KEY=example-openai-key-not-realfly secrets set GOOGLE_API_KEY=... # tokens de los canalesfly secrets set DISCORD_BOT_TOKEN=example-discord-bot-tokenLos enlaces que no sean local loopback (--bind lan) requieren una ruta válida de autenticación del Gateway. Este ejemplo utiliza OPENCLAW_GATEWAY_TOKEN, pero gateway.auth.password o un despliegue con proxy de confianza que no sea local loopback y esté configurado correctamente también cumplen el requisito. Consulta Gestión de secretos para conocer el contrato de SecretRef.
Trata estos tokens como contraseñas. Para las claves de API y los tokens, prefiere las variables de entorno/fly secrets al archivo de configuración, de modo que los secretos no se incluyan en openclaw.json.
Desplegar
fly deployEl primer despliegue compila la imagen de Docker. Comprueba el estado después del despliegue:
fly statusfly logsAl iniciarse, el Gateway registra gateway ready cuando el agente de escucha HTTP/WebSocket está activo. La comprobación de estado propia de Fly supervisa internal_port = 3000 según fly.toml; además, la directiva HEALTHCHECK de Docker de la imagen consulta periódicamente /healthz en su puerto predeterminado 18789, que aquí no se utiliza porque este despliegue sobrescribe el puerto del Gateway con --port 3000.
Crear el archivo de configuración
Accede a la máquina mediante SSH para crear una configuración adecuada:
fly ssh consolemkdir -p /datacat > /data/openclaw.json << 'EOF'{ "agents": { "defaults": { "model": { "primary": "anthropic/claude-opus-4-6", "fallbacks": ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4"] }, "maxConcurrent": 4 }, "list": [ { "id": "main", "default": true } ] }, "auth": { "profiles": { "anthropic:default": { "mode": "token", "provider": "anthropic" }, "openai:default": { "mode": "token", "provider": "openai" } } }, "bindings": [ { "agentId": "main", "match": { "channel": "discord" } } ], "channels": { "discord": { "enabled": true, "groupPolicy": "allowlist", "guilds": { "YOUR_GUILD_ID": { "channels": { "general": { "allow": true } }, "requireMention": false } } } }, "gateway": { "mode": "local", "bind": "auto", "controlUi": { "allowedOrigins": [ "https://my-openclaw.fly.dev", "http://localhost:3000", "http://127.0.0.1:3000" ] } }, "meta": {}}EOFCon OPENCLAW_STATE_DIR=/data, la ruta de configuración es /data/openclaw.json.
Reemplaza https://my-openclaw.fly.dev por el origen real de tu aplicación de Fly. Al iniciarse, el Gateway añade los orígenes locales de la interfaz de control a partir de los valores de --bind y --port en tiempo de ejecución, de modo que el primer arranque pueda continuar antes de que exista la configuración; sin embargo, para acceder desde el navegador a través de Fly, el origen HTTPS exacto debe figurar en gateway.controlUi.allowedOrigins.
El token de Discord puede proceder de cualquiera de estas fuentes:
- Variable de entorno
DISCORD_BOT_TOKEN(recomendada para secretos); no es necesario añadirla a la configuración, ya que el Gateway la lee automáticamente - Archivo de configuración
channels.discord.token
Reinicia para aplicar los cambios:
exitfly machine restart <machine-id>Acceder al Gateway
Interfaz de control
fly openO visita https://my-openclaw.fly.dev/.
Autentícate con el secreto compartido configurado: el token del Gateway de OPENCLAW_GATEWAY_TOKEN o tu contraseña si cambiaste a la autenticación mediante contraseña.
Registros
fly logs # registros en tiempo realfly logs --no-tail # registros recientesConsola SSH
fly ssh consoleSolución de problemas
"La aplicación no está escuchando en la dirección esperada"
El Gateway está enlazando con 127.0.0.1 en lugar de 0.0.0.0.
Solución: añade --bind lan al comando del proceso en fly.toml.
Fallan las comprobaciones de estado o se rechaza la conexión
Fly no puede acceder al Gateway en el puerto configurado.
Solución: asegúrate de que internal_port coincida con el puerto del Gateway (--port 3000 o OPENCLAW_GATEWAY_PORT=3000).
Problemas de memoria o falta de memoria
El contenedor se reinicia continuamente o el sistema lo termina. Indicadores: SIGABRT, v8::internal::Runtime_AllocateInYoungGeneration o reinicios silenciosos.
Solución: aumenta la memoria en fly.toml:
[[vm]] memory = "2048mb"O actualiza una máquina existente:
fly machine update <machine-id> --vm-memory 2048 -y512 MB es insuficiente. 1 GB puede funcionar, pero podría agotarse la memoria bajo carga o con registros detallados. Se recomiendan 2 GB.
Problemas con el bloqueo del Gateway
El Gateway se niega a iniciarse y muestra errores de tipo "ya está en ejecución" después de reiniciar un contenedor.
El archivo de bloqueo de instancia única se encuentra en <tmpdir>/openclaw-<uid>/gateway.<hash>.lock (en Linux: /tmp/openclaw-<uid>/gateway.<hash>.lock), no en el volumen persistente /data, por lo que un reinicio completo del contenedor normalmente lo elimina junto con el resto del sistema de archivos del contenedor. Si el bloqueo persiste (por ejemplo, tras ejecutar fly machine restart, que conserva el sistema de archivos del contenedor) e impide el inicio, elimínalo manualmente:
fly ssh console --command "rm -f /tmp/openclaw-*/gateway.*.lock"fly machine restart <machine-id>No se lee la configuración
--allow-unconfigured solo omite la protección de inicio. No crea ni repara /data/openclaw.json, así que asegúrate de que exista la configuración real y de que incluya "gateway": { "mode": "local" } para iniciar normalmente un Gateway local.
Comprueba que exista la configuración:
fly ssh console --command "cat /data/openclaw.json"Escribir la configuración mediante SSH
fly ssh console -C no admite la redirección del shell. Para escribir un archivo de configuración:
# echo + tee (canalización del equipo local al remoto)echo '{"your":"config"}' | fly ssh console -C "tee /data/openclaw.json" # o sftpfly sftp shell> put /local/path/config.json /data/openclaw.jsonfly sftp puede fallar si el archivo ya existe; elimínalo primero:
fly ssh console --command "rm /data/openclaw.json"El estado no persiste
Si pierdes los perfiles de autenticación, el estado de los canales/proveedores o las sesiones después de un reinicio, el directorio de estado se está escribiendo en el sistema de archivos del contenedor en lugar de en el volumen.
Solución: asegúrate de que OPENCLAW_STATE_DIR=/data esté configurado en fly.toml y vuelve a desplegar.
Actualización
git pullfly deployfly statusfly logsgit pull + fly deploy es la ruta supervisada en este caso: recompila la imagen a partir del Dockerfile, por lo que la versión de la CLI/del Gateway, la imagen base del sistema operativo y cualquier cambio en el Dockerfile se actualizan conjuntamente. Ejecutar openclaw update dentro del contenedor en ejecución no es la misma operación, ya que la imagen se distribuye como un árbol dist/ compilado con Docker, sin un checkout de .git ni una instalación global administrada por npm que pueda detectar; consulta Actualización para conocer ese flujo en instalaciones de tipo máquina virtual.
Actualizar el comando de la máquina
Para cambiar el comando de inicio sin realizar un despliegue completo:
fly machines listfly machine update <machine-id> --command "node dist/index.js gateway --port 3000 --bind lan" -y # o con un aumento de memoriafly machine update <machine-id> --vm-memory 2048 --command "node dist/index.js gateway --port 3000 --bind lan" -yUn fly deploy posterior restablece el comando de la máquina al valor definido en fly.toml; vuelve a aplicar los cambios manuales después de desplegar de nuevo.
Despliegue privado (reforzado)
De forma predeterminada, Fly asigna direcciones IP públicas, por lo que tu Gateway queda accesible en https://your-app.fly.dev y puede ser detectado por escáneres de Internet (Shodan, Censys, etc.).
Utiliza deploy/fly.private.toml para realizar un despliegue reforzado sin IP pública: omite [http_service], por lo que no se asigna ningún acceso público entrante.
Cuándo utilizar un despliegue privado
- Solo llamadas/mensajes salientes (sin Webhooks entrantes)
- Los túneles de ngrok o Tailscale gestionan las retrollamadas de los Webhooks
- Se accede al Gateway mediante SSH, un proxy o WireGuard en lugar de un navegador
- El despliegue debe permanecer oculto para los escáneres de Internet
Configuración
fly deploy -c deploy/fly.private.tomlO convierte un despliegue existente:
# enumera las IP actualesfly ips list -a my-openclaw # libera las IP públicasfly ips release <public-ipv4> -a my-openclawfly ips release <public-ipv6> -a my-openclaw # cambia a la configuración privada para que los despliegues futuros no vuelvan a asignar IP públicasfly deploy -c deploy/fly.private.toml # asigna una IPv6 exclusivamente privadafly ips allocate-v6 --private -a my-openclawDespués de esto, fly ips list debería mostrar solo una IP de tipo private:
VERSION IP TYPE REGIONv6 fdaa:x:x:x:x::x private globalAcceso a un despliegue privado
Opción 1: proxy local (la más sencilla)
fly proxy 3000:3000 -a my-openclaw# abrir http://localhost:3000 en un navegadorOpción 2: VPN WireGuard
fly wireguard create# importar en un cliente de WireGuard y acceder mediante la dirección IPv6 interna# ejemplo: http://[fdaa:x:x:x:x::x]:3000Opción 3: solo SSH
fly ssh console -a my-openclawWebhooks con un despliegue privado
Para las devoluciones de llamada de Webhook (Twilio, Telnyx, etc.) sin exposición pública:
- Túnel de ngrok: ejecuta ngrok dentro del contenedor o como contenedor auxiliar
- Tailscale Funnel: expón rutas específicas mediante Tailscale
- Solo salientes: algunos proveedores (Twilio) permiten realizar llamadas salientes sin webhooks
Ejemplo de configuración de llamadas de voz con ngrok, en plugins.entries.voice-call.config:
{ plugins: { entries: { "voice-call": { enabled: true, config: { provider: "twilio", tunnel: { provider: "ngrok" }, webhookSecurity: { allowedHosts: ["example.ngrok.app"], }, }, }, }, },}El túnel de ngrok se ejecuta dentro del contenedor y proporciona una URL pública de Webhook sin exponer la propia aplicación de Fly. Establece webhookSecurity.allowedHosts en el nombre de host del túnel para que se acepten los encabezados de host reenviados.
Consideraciones de seguridad
| Aspecto | Público | Privado |
|---|---|---|
| Escáneres de Internet | Detectable | Oculto |
| Ataques directos | Posibles | Bloqueados |
| Acceso a la IU de control | Navegador | Proxy/VPN |
| Entrega de webhooks | Directa | Mediante túnel |
Notas
- Fly.io usa la arquitectura x86; el Dockerfile es compatible tanto con x86 como con ARM.
- Para la incorporación de WhatsApp/Telegram, usa
fly ssh console. - Los datos persistentes se almacenan en el volumen ubicado en
/data. - Signal requiere signal-cli (una CLI basada en Java) en la imagen; usa una imagen personalizada y mantén la memoria en 2 GB o más.
Coste
Con la configuración recomendada (shared-cpu-2x, 2 GB de RAM), el coste estimado es de unos 10-15 USD al mes, según el uso; el nivel gratuito cubre parte de la asignación básica. Consulta los precios de Fly.io para conocer las tarifas actuales.
Siguientes pasos
- Configura los canales de mensajería: Canales
- Configura el Gateway: Configuración del Gateway
- Mantén OpenClaw actualizado: Actualización