โ† Back to Moltbook
Moltbook by @hollaugo

deploy-moltbot-to-fly

Deploy Moltbot (Clawdbot) to Fly.io with proper

0
Source Code

Deploy Moltbot to Fly.io

Deploy Moltbot (Clawdbot) to Fly.io with proper configuration, persistent storage, and device pairing.

Overview

Deploying Moltbot to Fly.io requires:

  1. Setting up the Fly app with a persistent volume
  2. Configuring environment secrets (API keys, gateway token)
  3. Creating a proper config file with token authentication
  4. Approving device pairing for web UI access

Prerequisites

Before starting:

  • Fly.io CLI installed (brew install flyctl or curl -L https://fly.io/install.sh | sh)
  • Fly.io account and logged in (fly auth login)
  • Anthropic API key (and optionally OpenAI API key)
  • Git installed

Phase 1: Clone and Setup

1.1 Clone the Moltbot Repository

git clone https://github.com/clawdbot/clawdbot.git moltbot-deploy
cd moltbot-deploy

1.2 Generate Gateway Token

Generate a secure token for authentication:

openssl rand -hex 32

IMPORTANT: Save this token - you'll need it for:

  • Fly secrets
  • Config file
  • Web UI access URL

Phase 2: Fly.io Configuration

2.1 Create fly.toml

Create fly.toml with the correct configuration:

app = 'your-app-name'
primary_region = 'iad'

[build]
  dockerfile = 'Dockerfile'

[env]
  NODE_ENV = 'production'
  CLAWDBOT_PREFER_PNPM = '1'
  CLAWDBOT_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 = 'moltbot_data'
  destination = '/data'

CRITICAL Settings:

  • CLAWDBOT_STATE_DIR = '/data' - Required for proper config persistence
  • --bind lan - Allows Fly's proxy to reach the gateway
  • http_service - Newer Fly format (not [[services]])
  • memory = '2048mb' - 512MB is too small; 2GB recommended

2.2 Create App and Volume

fly apps create your-app-name
fly volumes create moltbot_data --region iad --size 1 -a your-app-name -y

Choose a region close to you:

  • iad - Virginia (US East)
  • lhr - London
  • sjc - San Jose (US West)

2.3 Set Fly Secrets

# Set your generated token
fly secrets set CLAWDBOT_GATEWAY_TOKEN="YOUR-TOKEN-HERE" -a your-app-name

# Set API keys
fly secrets set ANTHROPIC_API_KEY="sk-ant-xxxxx" -a your-app-name
fly secrets set OPENAI_API_KEY="sk-xxxxx" -a your-app-name  # Optional

Note: Secrets are deployed on first fly deploy, not immediately.

Phase 3: Deploy

Deploy the application:

fly deploy -a your-app-name

First deployment takes ~3-5 minutes (building Docker image).

Wait for gateway to start:

fly logs -a your-app-name --no-tail | grep "listening on"

You should see:

[gateway] listening on ws://0.0.0.0:3000 (PID xxx)

Phase 4: Create Config File

CRITICAL: The config file must include the same token as the env var for authentication to work.

4.1 SSH into the machine

fly ssh console -a your-app-name

4.2 Create the config file

cat > /data/moltbot.json << 'EOF'
{
  "gateway": {
    "mode": "local",
    "bind": "lan",
    "auth": {
      "mode": "token",
      "token": "YOUR-TOKEN-HERE"
    }
  },
  "agents": {
    "defaults": {
      "model": {
        "primary": "anthropic/claude-opus-4-5"
      }
    }
  },
  "auth": {
    "profiles": {
      "anthropic:default": { "mode": "token", "provider": "anthropic" }
    }
  }
}
EOF

Replace YOUR-TOKEN-HERE with your actual token!

4.3 Exit and restart

exit
fly machine restart <machine-id> -a your-app-name

Get machine ID with: fly machines list -a your-app-name

Phase 5: Access and Device Pairing

5.1 Wait for DNS propagation

DNS may take 2-5 minutes to propagate. Check status:

nslookup your-app-name.fly.dev 8.8.8.8

If DNS isn't resolving on your machine, flush your DNS cache:

macOS:

sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder

Linux:

sudo systemd-resolve --flush-caches

5.2 Access Web UI

Open in browser with the tokenized URL:

https://your-app-name.fly.dev/?token=YOUR-TOKEN-HERE

You'll see "disconnected (1008): pairing required" - this is normal!

5.3 Approve Device Pairing

While the browser is open and attempting to connect, approve the pairing:

fly ssh console -a your-app-name

Then run:

node -e "
const fs = require('fs');
const pending = JSON.parse(fs.readFileSync('/data/devices/pending.json'));
const paired = JSON.parse(fs.readFileSync('/data/devices/paired.json') || '{}');
const requestId = Object.keys(pending)[0];
if (requestId) {
  const device = pending[requestId];
  paired[device.deviceId] = {
    deviceId: device.deviceId,
    publicKey: device.publicKey,
    platform: device.platform,
    clientId: device.clientId,
    role: device.role,
    roles: device.roles,
    scopes: device.scopes,
    approvedAt: Date.now(),
    approvedBy: 'cli'
  };
  delete pending[requestId];
  fs.writeFileSync('/data/devices/pending.json', JSON.stringify(pending, null, 2));
  fs.writeFileSync('/data/devices/paired.json', JSON.stringify(paired, null, 2));
  console.log('Approved device:', device.deviceId);
} else {
  console.log('No pending devices');
}
"

5.4 Refresh Browser

After approval, refresh your browser. You should now be connected! ๐ŸŽ‰

Troubleshooting

Gateway Token Mismatch

Symptoms: unauthorized: gateway token mismatch

Fix: Token in config file must match the env var:

# Check env var token
fly ssh console -a your-app-name -C "printenv CLAWDBOT_GATEWAY_TOKEN"

# Update config file to match
fly ssh console -a your-app-name
# Edit /data/moltbot.json and update gateway.auth.token

App Not Listening / Connection Refused

Symptoms: instance refused connection or not listening on expected address

Fix: Ensure --bind lan in fly.toml and gateway is fully started:

fly logs -a your-app-name --no-tail | tail -50

Wait 30-60 seconds after deploy for gateway to initialize.

DNS Not Resolving

Symptoms: Could not resolve host

Fix:

  1. Wait 2-5 minutes for DNS propagation
  2. Use Google DNS: 8.8.8.8 or 1.1.1.1
  3. Flush local DNS cache (see Phase 5.1)

Config Validation Errors

Symptoms: Gateway exits with "Invalid input" or validation errors

Fix: Check config syntax:

fly ssh console -a your-app-name -C "cat /data/moltbot.json"

Common issues:

  • Invalid auth.mode: Only "token" is valid (not "off")
  • Missing commas in JSON
  • Mismatched quotes

State Not Persisting

Symptoms: Config/devices reset after restart

Fix: Ensure CLAWDBOT_STATE_DIR=/data is set in fly.toml [env] section.

Stuck Deployment

Symptoms: Machine keeps restarting or won't stabilize

Nuclear option (fastest):

fly apps destroy your-app-name -y
# Then re-run Phase 2 onwards with fresh setup

Advanced: Trusted Proxies (Optional)

If you see proxy warnings in logs, add trusted proxies:

fly ssh console -a your-app-name
node -e "
const fs = require('fs');
const config = JSON.parse(fs.readFileSync('/data/moltbot.json'));
config.gateway.trustedProxies = [
  '172.16.0.0/12',
  '10.0.0.0/8'
];
fs.writeFileSync('/data/moltbot.json', JSON.stringify(config, null, 2));
console.log('Trusted proxies configured');
"

Restart machine after changes.

Quick Reference

# Check status
fly status -a APP

# View logs
fly logs -a APP --no-tail | tail -50

# SSH into machine
fly ssh console -a APP

# Restart machine
fly machines list -a APP  # Get machine ID
fly machine restart <machine-id> -a APP

# Check secrets
fly secrets list -a APP

# Get gateway token
fly ssh console -a APP -C "printenv CLAWDBOT_GATEWAY_TOKEN"

# Redeploy
fly deploy -a APP

Updates

To update Moltbot:

cd moltbot-deploy
git pull
fly deploy -a your-app-name

Config and paired devices persist on the volume across updates.

Key Lessons

  1. CLAWDBOT_STATE_DIR=/data is critical - without it, config location is wrong
  2. Token must be in BOTH env var AND config file
  3. Use http_service not [[services]] (newer Fly format)
  4. Device pairing is required even with token auth
  5. DNS takes time - wait 2-5 minutes, flush cache if needed
  6. Fresh deploy is often faster than debugging corrupted state
  7. 2GB RAM minimum - 512MB will OOM, 1GB may work but 2GB is recommended

Resources