Ir al contenido

Fly.io

Objetivo: OpenClaw Gateway ejecutándose en una máquina Fly.io con almacenamiento persistente, HTTPS automático y acceso a Discord/canales.

  • CLI flyctl instalada
  • Cuenta de Fly.io (la capa gratuita funciona)
  • Autenticación del modelo: clave de API de Anthropic (u otras claves de proveedores)
  • Credenciales del canal: token del bot de Discord, token de Telegram, etc.
  1. Clonar repositorio → personalizar fly.toml
  2. Crear app + volumen → establecer secretos
  3. Desplegar con fly deploy
  4. Entrar por SSH para crear la configuración o usar la Interfaz de Control
Ventana de terminal
# Clone the repo
git clone https://github.com/openclaw/openclaw.git
cd openclaw
# Create a new Fly app (pick your own name)
fly apps create my-openclaw
# Create a persistent volume (1GB is usually enough)
fly volumes create openclaw_data --size 1 --region iad

Consejo: Elige una región cercana a ti. Opciones comunes: lhr (Londres), iad (Virginia), sjc (San José).

Edita fly.toml para que coincida con el nombre de tu app y los requisitos.

Nota de seguridad: La configuración predeterminada expone una URL pública. Para un despliegue reforzado sin IP pública, consulta Despliegue Privado o usa fly.private.toml.

app = "my-openclaw" # Your app name
primary_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"

Configuraciones clave:

ConfiguraciónPor qué
--bind lanSe enlaza a 0.0.0.0 para que el proxy de Fly pueda alcanzar el gateway
--allow-unconfiguredSe inicia sin un archivo de configuración (crearás uno después)
internal_port = 3000Debe coincidir con --port 3000 (o OPENCLAW_GATEWAY_PORT) para los controles de salud de Fly
memory = "2048mb"512MB es muy poco; se recomiendan 2GB
OPENCLAW_STATE_DIR = "/data"Persiste el estado en el volumen
Ventana de terminal
# Required: Gateway token (for non-loopback binding)
fly secrets set OPENCLAW_GATEWAY_TOKEN=$(openssl rand -hex 32)
# Model provider API keys
fly secrets set ANTHROPIC_API_KEY=sk-ant-...
# Optional: Other providers
fly secrets set OPENAI_API_KEY=sk-...
fly secrets set GOOGLE_API_KEY=...
# Channel tokens
fly secrets set DISCORD_BOT_TOKEN=MTQ...

Notas:

  • Los enlaces no locales (--bind lan) requieren OPENCLAW_GATEWAY_TOKEN por seguridad.
  • Trata estos tokens como contraseñas.
  • Prefiere variables de entorno al archivo de configuración para todas las claves de API y tokens. Esto mantiene los secretos fuera de openclaw.json donde podrían exponerse o registrarse accidentalmente.
Ventana de terminal
fly deploy

El primer despliegue construye la imagen de Docker (~2-3 minutos). Los despliegues posteriores son más rápidos.

Después del despliegue, verifica:

Ventana de terminal
fly status
fly logs

Deberías ver:

[gateway] listening on ws://0.0.0.0:3000 (PID xxx)
[discord] logged in to discord as xxx

Entra por SSH a la máquina para crear una configuración adecuada:

Ventana de terminal
fly ssh console

Crea el directorio y el archivo de configuración:

Ventana de terminal
mkdir -p /data
cat > /data/openclaw.json << 'EOF'
{
"agents": {
"defaults": {
"model": {
"primary": "anthropic/claude-opus-4-5",
"fallbacks": ["anthropic/claude-sonnet-4-5", "openai/gpt-4o"]
},
"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"
},
"meta": {
"lastTouchedVersion": "2026.1.29"
}
}
EOF

Nota: Con OPENCLAW_STATE_DIR=/data, la ruta de configuración es /data/openclaw.json.

Nota: El token de Discord puede provenir de:

  • Variable de entorno: DISCORD_BOT_TOKEN (recomendado para secretos)
  • Archivo de configuración: channels.discord.token

Si usa una variable de entorno, no es necesario agregar el token a la configuración. El gateway lee DISCORD_BOT_TOKEN automáticamente.

Reinicie para aplicar:

Ventana de terminal
exit
fly machine restart <machine-id>

Abrir en el navegador:

Ventana de terminal
fly open

O visite https://my-openclaw.fly.dev/

Pegue su token del gateway (el de OPENCLAW_GATEWAY_TOKEN) para autenticarse.

Ventana de terminal
fly logs # Live logs
fly logs --no-tail # Recent logs
Ventana de terminal
fly ssh console

”La aplicación no está escuchando en la dirección esperada”

Sección titulada «”La aplicación no está escuchando en la dirección esperada”»

El gateway se está vinculando a 127.0.0.1 en lugar de 0.0.0.0.

Solución: Agregue --bind lan a su comando de proceso en fly.toml.

Fallo en las comprobaciones de salud / conexión rechazada

Sección titulada «Fallo en las comprobaciones de salud / conexión rechazada»

Fly no puede alcanzar el gateway en el puerto configurado.

Solución: Asegúrese de que internal_port coincida con el puerto del gateway (configure --port 3000 o OPENCLAW_GATEWAY_PORT=3000).

El contenedor se reinicia o se elimina repetidamente. Signos: SIGABRT, v8::internal::Runtime_AllocateInYoungGeneration o reinicios silenciosos.

Solución: Aumente la memoria en fly.toml:

[[vm]]
memory = "2048mb"

O actualice una máquina existente:

Ventana de terminal
fly machine update <machine-id> --vm-memory 2048 -y

Nota: 512MB es muy poco. 1GB puede funcionar pero puede dar errores OOM bajo carga o con registro detallado. Se recomiendan 2GB.

El Gateway se niega a iniciarse con errores de “ya está en ejecución”.

Esto sucede cuando el contenedor se reinicia pero el archivo de bloqueo PID persiste en el volumen.

Solución: Elimine el archivo de bloqueo:

Ventana de terminal
fly ssh console --command "rm -f /data/gateway.*.lock"
fly machine restart <machine-id>

El archivo de bloqueo está en /data/gateway.*.lock (no en un subdirectorio).

Si usa --allow-unconfigured, el gateway crea una configuración mínima. Su configuración personalizada en /data/openclaw.json debería leerse al reiniciar.

Verifique que la configuración exista:

Ventana de terminal
fly ssh console --command "cat /data/openclaw.json"

El comando fly ssh console -C no admite el redireccionamiento de shell. Para escribir un archivo de configuración:

Ventana de terminal
# Use echo + tee (pipe from local to remote)
echo '{"your":"config"}' | fly ssh console -C "tee /data/openclaw.json"
# Or use sftp
fly sftp shell
> put /local/path/config.json /data/openclaw.json

Nota: fly sftp puede fallar si el archivo ya existe. Elimínelo primero:

Ventana de terminal
fly ssh console --command "rm /data/openclaw.json"

Si pierde las credenciales o las sesiones después de un reinicio, el directorio de estado se está escribiendo en el sistema de archivos del contenedor.

Solución: Asegúrese de que OPENCLAW_STATE_DIR=/data esté configurado en fly.toml y vuelva a implementar.

Ventana de terminal
# Pull latest changes
git pull
# Redeploy
fly deploy
# Check health
fly status
fly logs

Si necesita cambiar el comando de inicio sin una reimplementación completa:

Ventana de terminal
# Get machine ID
fly machines list
# Update command
fly machine update <machine-id> --command "node dist/index.js gateway --port 3000 --bind lan" -y
# Or with memory increase
fly machine update <machine-id> --vm-memory 2048 --command "node dist/index.js gateway --port 3000 --bind lan" -y

Nota: Después de fly deploy, el comando de máquina puede restablecerse a lo que está en fly.toml. Si realizó cambios manuales, aplíquelos nuevamente después de la implementación.

Implementación privada (Refuerzo de seguridad)

Sección titulada «Implementación privada (Refuerzo de seguridad)»

De forma predeterminada, Fly asigna direcciones IP públicas, lo que hace que su puerta de enlace sea accesible en https://your-app.fly.dev. Esto es conveniente, pero significa que su implementación es discoverable por escáneres de Internet (Shodan, Censys, etc.).

Para una implementación reforzada con sin exposición pública, utilice la plantilla privada.

  • Solo realiza llamadas/mensajes salientes (sin webhooks entrantes)
  • Utiliza túneles ngrok o Tailscale para cualquier devolución de llamada de webhook
  • Accede a la puerta de enlace a través de SSH, proxy o WireGuard en lugar del navegador
  • Desea que la implementación esté oculta para los escáneres de Internet

Use fly.private.toml en lugar de la configuración estándar:

Ventana de terminal
# Deploy with private config
fly deploy -c fly.private.toml

O convierta una implementación existente:

Ventana de terminal
# List current IPs
fly ips list -a my-openclaw
# Release public IPs
fly ips release <public-ipv4> -a my-openclaw
fly ips release <public-ipv6> -a my-openclaw
# Switch to private config so future deploys don't re-allocate public IPs
# (remove [http_service] or deploy with the private template)
fly deploy -c fly.private.toml
# Allocate private-only IPv6
fly ips allocate-v6 --private -a my-openclaw

Después de esto, fly ips list debería mostrar solo una IP de tipo private:

VERSION IP TYPE REGION
v6 fdaa:x:x:x:x::x private global

Dado que no hay una URL pública, use uno de estos métodos:

Opción 1: Proxy local (el más simple)

Ventana de terminal
# Forward local port 3000 to the app
fly proxy 3000:3000 -a my-openclaw
# Then open http://localhost:3000 in browser

Opción 2: VPN WireGuard

Ventana de terminal
# Create WireGuard config (one-time)
fly wireguard create
# Import to WireGuard client, then access via internal IPv6
# Example: http://[fdaa:x:x:x:x::x]:3000

Opción 3: Solo SSH

Ventana de terminal
fly ssh console -a my-openclaw

Si necesita devoluciones de llamada de webhook (Twilio, Telnyx, etc.) sin exposición pública:

  1. Túnel ngrok - Ejecute ngrok dentro del contenedor o como sidecar
  2. Tailscale Funnel - Exponer rutas específicas a través de Tailscale
  3. Solo saliente - Algunos proveedores (Twilio) funcionan bien para llamadas salientes sin webhooks

Ejemplo de configuración de llamada de voz con ngrok:

{
"plugins": {
"entries": {
"voice-call": {
"enabled": true,
"config": {
"provider": "twilio",
"tunnel": { "provider": "ngrok" },
"webhookSecurity": {
"allowedHosts": ["example.ngrok.app"]
}
}
}
}
}
}

El túnel ngrok se ejecuta dentro del contenedor y proporciona una URL de webhook pública sin exponer la aplicación Fly en sí. Establezca webhookSecurity.allowedHosts en el nombre de host del túnel público para que se acepten los encabezados de host reenviados.

AspectoPúblicoPrivado
Escáneres de InternetDetectableOculto
Ataques directosPosibleBloqueado
Acceso a la interfaz de controlNavegadorProxy/VPN
Entrega de webhooksDirectaVía túnel
  • Fly.io utiliza arquitectura x86 (no ARM)
  • El Dockerfile es compatible con ambas arquitecturas
  • Para el incorporado de WhatsApp/Telegram, use fly ssh console
  • Los datos persistentes residen en el volumen en /data
  • Signal requiere Java + signal-cli; use una imagen personalizada y mantenga la memoria en 2GB o más.

Con la configuración recomendada (shared-cpu-2x, 2GB RAM):

  • ~$10-15/mes dependiendo del uso
  • El nivel gratuito incluye alguna asignación

Consulte Fly.io pricing para obtener detalles.