Deploy su Fly.io

Obiettivo: Gateway OpenClaw in esecuzione su una macchina Fly.io con storage persistente, HTTPS automatico e accesso ai canali Discord e altri.

Cosa ti serve

  • flyctl CLI installato
  • Account Fly.io (il piano gratuito funziona)
  • Autenticazione modello: chiave API per il provider di modelli scelto
  • Credenziali canale: token bot Discord, token Telegram, ecc.

Percorso rapido per principianti

  1. Clona il repo → personalizza fly.toml
  2. Crea app + volume → imposta i segreti
  3. Deploy con fly deploy
  4. Entra via SSH per creare la configurazione o usa la Control UI

1) Crea l’app Fly

# Clona il repository
git clone https://github.com/openclaw/openclaw.git
cd openclaw

# Crea una nuova app Fly (scegli il tuo nome)
fly apps create my-openclaw

# Crea un volume persistente (1GB di solito basta)
fly volumes create openclaw_data --size 1 --region iad

Suggerimento: Scegli una regione vicina a te. Opzioni comuni: lhr (Londra), iad (Virginia), sjc (San Jose).

2) Configura fly.toml

Modifica fly.toml per farlo corrispondere al nome della tua app e ai tuoi requisiti.

Nota sulla sicurezza: La configurazione predefinita espone un URL pubblico. Per un deploy blindato senza IP pubblico, vedi Deploy privato o usa fly.private.toml.

app = "my-openclaw"  # Il nome della tua app
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"

Impostazioni chiave:

ImpostazionePerche
--bind lanFa il bind su 0.0.0.0 cosi il proxy Fly puo raggiungere il gateway
--allow-unconfiguredAvvia senza file di configurazione (lo creerai dopo)
internal_port = 3000Deve corrispondere a --port 3000 (o OPENCLAW_GATEWAY_PORT) per gli health check di Fly
memory = "2048mb"512MB sono troppo pochi; consigliati 2GB
OPENCLAW_STATE_DIR = "/data"Persiste lo stato sul volume

3) Imposta i segreti

# Obbligatorio: token del gateway (per il binding non-loopback)
fly secrets set OPENCLAW_GATEWAY_TOKEN=$(openssl rand -hex 32)

# Chiavi API del provider di modelli
fly secrets set ANTHROPIC_API_KEY=sk-ant-...

# Opzionale: altri provider
fly secrets set OPENAI_API_KEY=sk-...
fly secrets set GOOGLE_API_KEY=...

# Token dei canali
fly secrets set DISCORD_BOT_TOKEN=MTQ...

Note:

  • I binding non-loopback (--bind lan) richiedono OPENCLAW_GATEWAY_TOKEN per sicurezza.
  • Tratta questi token come password.
  • Preferisci le variabili d’ambiente al file di configurazione per tutte le chiavi API e i token. Questo tiene i segreti fuori da openclaw.json dove potrebbero essere esposti o registrati nei log accidentalmente.

4) Deploy

fly deploy

Il primo deploy compila l’immagine Docker (~2-3 minuti). I successivi sono piu rapidi.

Dopo il deploy, verifica:

fly status
fly logs

Dovresti vedere:

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

5) Crea il file di configurazione

Entra nella macchina via SSH:

fly ssh console

Crea la directory e il file di configurazione:

mkdir -p /data
cat > /data/openclaw.json << 'EOF'
{
  "agents": {
    "defaults": {
      "model": {
        "primary": "anthropic/claude-opus-4-6",
        "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, il percorso della configurazione e /data/openclaw.json.

Nota: Il token Discord puo provenire da:

  • Variabile d’ambiente: DISCORD_BOT_TOKEN (consigliato per i segreti)
  • File di configurazione: channels.discord.token

Se usi la variabile d’ambiente, non serve aggiungere il token alla configurazione. Il gateway legge DISCORD_BOT_TOKEN automaticamente.

Riavvia per applicare:

exit
fly machine restart <machine-id>

6) Accedi al Gateway

Control UI

Apri nel browser:

fly open

Oppure visita https://my-openclaw.fly.dev/

Incolla il token del gateway (quello di OPENCLAW_GATEWAY_TOKEN) per autenticarti.

Log

fly logs              # Log in tempo reale
fly logs --no-tail    # Log recenti

Console SSH

fly ssh console

Risoluzione problemi

”App is not listening on expected address”

Il gateway sta facendo il bind su 127.0.0.1 invece che su 0.0.0.0.

Soluzione: Aggiungi --bind lan al comando del processo in fly.toml.

Health check falliti / connessione rifiutata

Fly non riesce a raggiungere il gateway sulla porta configurata.

Soluzione: Assicurati che internal_port corrisponda alla porta del gateway (imposta --port 3000 o OPENCLAW_GATEWAY_PORT=3000).

OOM / Problemi di memoria

Il container continua a riavviarsi o viene terminato. Segnali: SIGABRT, v8::internal::Runtime_AllocateInYoungGeneration, o riavvii silenziosi.

Soluzione: Aumenta la memoria in fly.toml:

[[vm]]
  memory = "2048mb"

Oppure aggiorna una macchina esistente:

fly machine update <machine-id> --vm-memory 2048 -y

Nota: 512MB sono troppo pochi. 1GB puo funzionare ma rischia OOM sotto carico o con logging verboso. Si consigliano 2GB.

Problemi con il lock del Gateway

Il gateway rifiuta di avviarsi con errori “already running”.

Succede quando il container si riavvia ma il file di lock PID persiste sul volume.

Soluzione: Elimina il file di lock:

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

Il file di lock si trova in /data/gateway.*.lock (non in una sottodirectory).

La configurazione non viene letta

Se usi --allow-unconfigured, il gateway crea una configurazione minimale. La tua configurazione personalizzata in /data/openclaw.json dovrebbe essere letta al riavvio.

Verifica che la configurazione esista:

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

Scrittura della configurazione via SSH

Il comando fly ssh console -C non supporta la redirezione della shell. Per scrivere un file di configurazione:

# Usa echo + tee (pipe dal locale al remoto)
echo '{"your":"config"}' | fly ssh console -C "tee /data/openclaw.json"

# Oppure usa sftp
fly sftp shell
> put /local/path/config.json /data/openclaw.json

Nota: fly sftp potrebbe fallire se il file esiste gia. Eliminalo prima:

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

Lo stato non persiste

Se perdi credenziali o sessioni dopo un riavvio, la directory di stato sta scrivendo sul filesystem del container.

Soluzione: Assicurati che OPENCLAW_STATE_DIR=/data sia impostato in fly.toml e rideploya.

Aggiornamenti

# Scarica le ultime modifiche
git pull

# Rideploya
fly deploy

# Verifica lo stato
fly status
fly logs

Aggiornamento del comando della macchina

Se devi cambiare il comando di avvio senza un redeploy completo:

# Ottieni l'ID della macchina
fly machines list

# Aggiorna il comando
fly machine update <machine-id> --command "node dist/index.js gateway --port 3000 --bind lan" -y

# Oppure con aumento di memoria
fly machine update <machine-id> --vm-memory 2048 --command "node dist/index.js gateway --port 3000 --bind lan" -y

Nota: Dopo fly deploy, il comando della macchina potrebbe resettarsi a quanto specificato in fly.toml. Se hai fatto modifiche manuali, riapplicale dopo il deploy.

Deploy privato (blindato)

Per impostazione predefinita, Fly assegna IP pubblici, rendendo il tuo gateway accessibile a https://your-app.fly.dev. Comodo, ma il deploy diventa individuabile da scanner Internet (Shodan, Censys, ecc.).

Per un deploy blindato senza esposizione pubblica, usa il template privato.

Quando usare il deploy privato

  • Fai solo chiamate/messaggi in uscita (nessun webhook in ingresso)
  • Usi tunnel ngrok o Tailscale per i callback dei webhook
  • Accedi al gateway via SSH, proxy o WireGuard invece che dal browser
  • Vuoi che il deploy sia nascosto dagli scanner Internet

Setup

Usa fly.private.toml al posto della configurazione standard:

# Deploy con configurazione privata
fly deploy -c fly.private.toml

Oppure converti un deploy esistente:

# Elenca gli IP attuali
fly ips list -a my-openclaw

# Rilascia gli IP pubblici
fly ips release <public-ipv4> -a my-openclaw
fly ips release <public-ipv6> -a my-openclaw

# Passa alla configurazione privata cosi i deploy futuri non riallocano IP pubblici
# (rimuovi [http_service] o deploya con il template privato)
fly deploy -c fly.private.toml

# Assegna un IPv6 solo privato
fly ips allocate-v6 --private -a my-openclaw

Dopo, fly ips list dovrebbe mostrare solo un IP di tipo private:

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

Accedere a un deploy privato

Senza URL pubblico, usa uno di questi metodi:

Opzione 1: Proxy locale (il piu semplice)

# Inoltra la porta locale 3000 all'app
fly proxy 3000:3000 -a my-openclaw

# Poi apri http://localhost:3000 nel browser

Opzione 2: VPN WireGuard

# Crea la configurazione WireGuard (una tantum)
fly wireguard create

# Importa nel client WireGuard, poi accedi tramite IPv6 interno
# Esempio: http://[fdaa:x:x:x:x::x]:3000

Opzione 3: Solo SSH

fly ssh console -a my-openclaw

Webhook con deploy privato

Se hai bisogno di callback webhook (Twilio, Telnyx, ecc.) senza esposizione pubblica:

  1. Tunnel ngrok - Esegui ngrok dentro il container o come sidecar
  2. Tailscale Funnel - Esponi percorsi specifici tramite Tailscale
  3. Solo in uscita - Alcuni provider (Twilio) funzionano bene per chiamate in uscita senza webhook

Esempio di configurazione voice-call con ngrok:

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

Il tunnel ngrok gira dentro il container e fornisce un URL webhook pubblico senza esporre l’app Fly stessa. Imposta webhookSecurity.allowedHosts sull’hostname del tunnel pubblico affinche gli header host inoltrati vengano accettati.

Vantaggi di sicurezza

AspettoPubblicoPrivato
Scanner InternetIndividuabileNascosto
Attacchi direttiPossibiliBloccati
Accesso Control UIBrowserProxy/VPN
Consegna webhookDirettaVia tunnel

Note

  • Fly.io usa architettura x86 (non ARM)
  • Il Dockerfile e compatibile con entrambe le architetture
  • Per l’onboarding WhatsApp/Telegram, usa fly ssh console
  • I dati persistenti risiedono sul volume in /data
  • Signal richiede Java + signal-cli; usa un’immagine personalizzata e mantieni la memoria a 2GB+.

Costi

Con la configurazione consigliata (shared-cpu-2x, 2GB RAM):

  • ~$10-15/mese a seconda dell’uso
  • Il piano gratuito include una certa quantita

Vedi Prezzi Fly.io per i dettagli.