Deploy trên Fly.io
Mục tiêu: Chạy OpenClaw Gateway trên máy Fly.io với lưu trữ bền vững, HTTPS tự động, và kết nối Discord/channel.
Những thứ các bạn cần
- flyctl CLI đã cài đặt
- Tài khoản Fly.io (free tier cũng được)
- Xác thực model: Anthropic API key (hoặc key của provider khác)
- Thông tin đăng nhập channel: Discord bot token, Telegram token, v.v.
Hướng dẫn nhanh cho người mới
- Clone repo → tùy chỉnh
fly.toml - Tạo app + volume → thiết lập secrets
- Deploy bằng
fly deploy - SSH vào để tạo config hoặc dùng Control UI
1) Tạo Fly app
# Clone repo về
git clone https://github.com/openclaw/openclaw.git
cd openclaw
# Tạo Fly app mới (đặt tên tùy thích)
fly apps create my-openclaw
# Tạo volume lưu trữ bền vững (1GB thường là đủ)
fly volumes create openclaw_data --size 1 --region iad
Mẹo: Chọn region gần các bạn nhất. Các lựa chọn phổ biến: lhr (London), iad (Virginia), sjc (San Jose).
2) Cấu hình fly.toml
Sửa file fly.toml để khớp với tên app và yêu cầu của các bạn.
Lưu ý bảo mật: Config mặc định sẽ expose một URL công khai. Để deploy an toàn hơn mà không có public IP, xem phần Private Deployment hoặc dùng fly.private.toml.
app = "my-openclaw" # Tên app của bạn
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"
Các thiết lập quan trọng:
| Thiết lập | Tại sao |
|---|---|
--bind lan | Bind tới 0.0.0.0 để proxy của Fly có thể kết nối tới gateway |
--allow-unconfigured | Khởi động mà không cần file config (các bạn sẽ tạo sau) |
internal_port = 3000 | Phải khớp với --port 3000 (hoặc OPENCLAW_GATEWAY_PORT) để health check |
memory = "2048mb" | 512MB quá nhỏ; mình khuyên dùng 2GB |
OPENCLAW_STATE_DIR = "/data" | Lưu trữ state trên volume |
3) Thiết lập secrets
# Bắt buộc: Gateway token (cho non-loopback binding)
fly secrets set OPENCLAW_GATEWAY_TOKEN=$(openssl rand -hex 32)
# API keys của model provider
fly secrets set ANTHROPIC_API_KEY=sk-ant-...
# Tùy chọn: Các provider khác
fly secrets set OPENAI_API_KEY=sk-...
fly secrets set GOOGLE_API_KEY=...
# Channel tokens
fly secrets set DISCORD_BOT_TOKEN=MTQ...
Lưu ý:
- Non-loopback binds (
--bind lan) yêu cầuOPENCLAW_GATEWAY_TOKENđể bảo mật. - Coi các token này như mật khẩu nhé.
- Ưu tiên dùng env vars thay vì config file cho tất cả API keys và tokens. Điều này giúp giữ secrets ra khỏi
openclaw.jsonnơi chúng có thể bị expose hoặc log nhầm.
4) Deploy
fly deploy
Lần deploy đầu tiên sẽ build Docker image (~2-3 phút). Các lần sau sẽ nhanh hơn.
Sau khi deploy xong, kiểm tra:
fly status
fly logs
Các bạn sẽ thấy:
[gateway] listening on ws://0.0.0.0:3000 (PID xxx)
[discord] logged in to discord as xxx
5) Tạo file config
SSH vào máy để tạo config đầy đủ:
fly ssh console
Tạo thư mục config và file:
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
Lưu ý: Với OPENCLAW_STATE_DIR=/data, đường dẫn config sẽ là /data/openclaw.json.
Lưu ý: Discord token có thể lấy từ:
- Biến môi trường:
DISCORD_BOT_TOKEN(khuyên dùng cho secrets) - File config:
channels.discord.token
Nếu dùng env var thì không cần thêm token vào config. Gateway sẽ tự đọc DISCORD_BOT_TOKEN.
Restart để áp dụng:
exit
fly machine restart <machine-id>
6) Truy cập Gateway
Control UI
Mở trong browser:
fly open
Hoặc truy cập https://my-openclaw.fly.dev/
Dán gateway token của các bạn (cái từ OPENCLAW_GATEWAY_TOKEN) để xác thực.
Logs
fly logs # Live logs
fly logs --no-tail # Logs gần đây
SSH Console
fly ssh console
Troubleshooting
”App is not listening on expected address”
Gateway đang bind tới 127.0.0.1 thay vì 0.0.0.0.
Cách sửa: Thêm --bind lan vào process command trong fly.toml.
Health checks failing / connection refused
Fly không thể kết nối tới gateway trên port đã cấu hình.
Cách sửa: Đảm bảo internal_port khớp với gateway port (set --port 3000 hoặc OPENCLAW_GATEWAY_PORT=3000).
OOM / Memory Issues
Container liên tục restart hoặc bị kill. Dấu hiệu: SIGABRT, v8::internal::Runtime_AllocateInYoungGeneration, hoặc restart im lặng.
Cách sửa: Tăng memory trong fly.toml:
[[vm]]
memory = "2048mb"
Hoặc update máy đang chạy:
fly machine update <machine-id> --vm-memory 2048 -y
Lưu ý: 512MB quá nhỏ. 1GB có thể chạy được nhưng dễ bị OOM khi load cao hoặc logging nhiều. Mình khuyên dùng 2GB.
Gateway Lock Issues
Gateway từ chối khởi động với lỗi “already running”.
Điều này xảy ra khi container restart nhưng PID lock file vẫn còn trên volume.
Cách sửa: Xóa lock file:
fly ssh console --command "rm -f /data/gateway.*.lock"
fly machine restart <machine-id>
Lock file nằm ở /data/gateway.*.lock (không phải trong thư mục con).
Config Not Being Read
Nếu dùng --allow-unconfigured, gateway sẽ tạo một config tối thiểu. Config tùy chỉnh của các bạn ở /data/openclaw.json sẽ được đọc khi restart.
Kiểm tra config có tồn tại không:
fly ssh console --command "cat /data/openclaw.json"
Writing Config via SSH
Lệnh fly ssh console -C không hỗ trợ shell redirection. Để ghi config file:
# Dùng echo + tee (pipe từ local sang remote)
echo '{"your":"config"}' | fly ssh console -C "tee /data/openclaw.json"
# Hoặc dùng sftp
fly sftp shell
> put /local/path/config.json /data/openclaw.json
Lưu ý: fly sftp có thể fail nếu file đã tồn tại. Xóa trước:
fly ssh console --command "rm /data/openclaw.json"
State Not Persisting
Nếu các bạn mất credentials hoặc sessions sau khi restart, state dir đang ghi vào filesystem của container.
Cách sửa: Đảm bảo OPENCLAW_STATE_DIR=/data được set trong fly.toml và redeploy.
Updates
# Pull các thay đổi mới nhất
git pull
# Redeploy
fly deploy
# Kiểm tra health
fly status
fly logs
Updating Machine Command
Nếu các bạn cần thay đổi startup command mà không cần redeploy toàn bộ:
# Lấy machine ID
fly machines list
# Update command
fly machine update <machine-id> --command "node dist/index.js gateway --port 3000 --bind lan" -y
# Hoặc kèm tăng memory
fly machine update <machine-id> --vm-memory 2048 --command "node dist/index.js gateway --port 3000 --bind lan" -y
Lưu ý: Sau fly deploy, machine command có thể reset về giá trị trong fly.toml. Nếu các bạn đã thay đổi thủ công, hãy áp dụng lại sau khi deploy.
Private Deployment (Hardened)
Mặc định, Fly cấp phát public IPs, khiến gateway của các bạn có thể truy cập tại https://your-app.fly.dev. Điều này tiện lợi nhưng có nghĩa là deployment của các bạn có thể bị phát hiện bởi các internet scanners (Shodan, Censys, v.v.).
Để có deployment an toàn hơn không có public exposure, dùng private template.
Khi nào dùng private deployment
- Các bạn chỉ thực hiện các cuộc gọi/tin nhắn outbound (không có inbound webhooks)
- Các bạn dùng ngrok hoặc Tailscale tunnels cho bất kỳ webhook callbacks nào
- Các bạn truy cập gateway qua SSH, proxy, hoặc WireGuard thay vì browser
- Các bạn muốn deployment ẩn khỏi internet scanners
Setup
Dùng fly.private.toml thay vì config tiêu chuẩn:
# Deploy với private config
fly deploy -c fly.private.toml
Hoặc chuyển đổi deployment hiện tại:
# Liệt kê các IPs hiện tại
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
# Chuyển sang private config để các lần deploy sau không cấp phát lại public IPs
# (xóa [http_service] hoặc deploy với private template)
fly deploy -c fly.private.toml
# Cấp phát private-only IPv6
fly ips allocate-v6 --private -a my-openclaw
Sau đó, fly ips list sẽ chỉ hiển thị IP loại private:
VERSION IP TYPE REGION
v6 fdaa:x:x:x:x::x private global
Accessing a private deployment
Vì không có public URL, dùng một trong các cách sau:
Option 1: Local proxy (đơn giản nhất)
# Forward local port 3000 tới app
fly proxy 3000:3000 -a my-openclaw
# Sau đó mở http://localhost:3000 trong browser
Option 2: WireGuard VPN
# Tạo WireGuard config (một lần)
fly wireguard create
# Import vào WireGuard client, sau đó truy cập qua internal IPv6
# Ví dụ: http://[fdaa:x:x:x:x::x]:3000
Option 3: SSH only
fly ssh console -a my-openclaw
Webhooks with private deployment
Nếu các bạn cần webhook callbacks (Twilio, Telnyx, v.v.) mà không muốn public exposure:
- ngrok tunnel - Chạy ngrok bên trong container hoặc như một sidecar
- Tailscale Funnel - Expose các paths cụ thể qua Tailscale
- Outbound-only - Một số providers (Twilio) hoạt động tốt cho outbound calls mà không cần webhooks
Ví dụ voice-call config với ngrok:
{
"plugins": {
"entries": {
"voice-call": {
"enabled": true,
"config": {
"provider": "twilio",
"tunnel": { "provider": "ngrok" }
}
}
}
}
}
Ngrok tunnel chạy bên trong container và cung cấp public webhook URL mà không expose Fly app.
Security benefits
| Khía cạnh | Public | Private |
|---|---|---|
| Internet scanners | Có thể phát hiện | Ẩn |
| Direct attacks | Có thể | Bị chặn |
| Control UI access | Browser | Proxy/VPN |
| Webhook delivery | Trực tiếp | Qua tunnel |
Notes
- Fly.io dùng x86 architecture (không phải ARM)
- Dockerfile tương thích với cả hai kiến trúc
- Để onboard WhatsApp/Telegram, dùng
fly ssh console - Dữ liệu bền vững được lưu trên volume tại
/data - Signal yêu cầu Java + signal-cli; dùng custom image và giữ memory ở 2GB+.
Cost
Với config mình khuyên dùng (shared-cpu-2x, 2GB RAM):
- ~$10-15/tháng tùy vào mức sử dụng
- Free tier bao gồm một số allowance
Xem Fly.io pricing để biết chi tiết.