Fly.io 배포
목표: 영구 스토리지, 자동 HTTPS, Discord/기타 채널 접근을 갖춘 Fly.io 머신에서 OpenClaw Gateway를 실행합니다.준비 사항
- flyctl CLI 설치
- Fly.io 계정(무료 티어 가능)
- 모델 인증 정보: 사용할 모델 제공자의 API 키
- 채널 자격 증명: Discord bot token, Telegram token 등
빠른 경로
- 저장소를 클론한 뒤
fly.toml을 수정합니다. - 앱과 볼륨을 만들고 시크릿을 설정합니다.
fly deploy로 배포합니다.- SSH로 접속해 config를 만들거나 Control UI를 사용합니다.
1) Fly 앱 만들기
lhr(런던), iad(버지니아), sjc(산호세)입니다.
2) fly.toml 구성
앱 이름과 운영 환경에 맞게 fly.toml을 수정합니다.
보안 참고: 기본 설정은 public URL을 노출합니다. public IP 없이 더 단단하게 운영하려면 비공개 배포를 참고하거나 fly.private.toml을 사용하세요.
| Setting | 이유 |
|---|---|
--bind lan | Fly 프록시가 Gateway에 접근할 수 있도록 0.0.0.0에 바인딩합니다. |
--allow-unconfigured | config 파일 없이 먼저 기동하고, 이후 config를 생성할 수 있게 합니다. |
internal_port = 3000 | Fly 헬스 체크가 보는 포트로, --port 3000 또는 OPENCLAW_GATEWAY_PORT와 일치해야 합니다. |
memory = "2048mb" | 512MB는 너무 작고, 2GB를 권장합니다. |
OPENCLAW_STATE_DIR = "/data" | 상태 데이터를 볼륨에 영구 저장합니다. |
3) 시크릿 설정
- non-loopback 바인딩(
--bind lan)에는 보안을 위해OPENCLAW_GATEWAY_TOKEN이 필요합니다. - 이 토큰들은 비밀번호처럼 다루세요.
- API 키와 토큰은 가능한 한
openclaw.json이 아니라 환경 변수로 관리하세요. 이렇게 하면 시크릿이 config 파일에 남거나 로그에 노출될 위험을 줄일 수 있습니다.
4) 배포
5) config 파일 만들기
정식 config를 만들기 위해 머신에 SSH로 접속합니다.OPENCLAW_STATE_DIR=/data를 사용하면 config 경로는 /data/openclaw.json입니다.
참고: Discord token은 다음 두 방식 중 하나로 제공할 수 있습니다.
- 환경 변수:
DISCORD_BOT_TOKEN(시크릿에는 이 방식을 권장) - config 파일:
channels.discord.token
DISCORD_BOT_TOKEN을 자동으로 읽습니다.
적용하려면 재시작합니다.
6) Gateway 접근
Control UI
브라우저에서 다음을 엽니다.https://my-openclaw.fly.dev/로 직접 접속합니다.
인증에는 OPENCLAW_GATEWAY_TOKEN에 넣은 gateway token을 사용합니다.
Logs
SSH Console
문제 해결
”App is not listening on expected address”
Gateway가0.0.0.0이 아니라 127.0.0.1에 바인딩된 상태입니다.
해결: fly.toml의 process command에 --bind lan을 추가하세요.
Health checks failing / connection refused
Fly가 설정된 포트에서 Gateway에 접근하지 못하고 있습니다. 해결:internal_port가 gateway 포트와 정확히 일치하는지 확인하세요. 예를 들어 --port 3000 또는 OPENCLAW_GATEWAY_PORT=3000이어야 합니다.
OOM / Memory Issues
컨테이너가 계속 재시작되거나 강제 종료됩니다.SIGABRT, v8::internal::Runtime_AllocateInYoungGeneration, 또는 조용한 재시작이 보일 수 있습니다.
해결: fly.toml에서 메모리를 늘리세요.
Gateway Lock Issues
Gateway가 “already running” 오류와 함께 시작을 거부합니다. 컨테이너는 재시작됐지만 PID lock 파일이 볼륨에 남아 있을 때 발생합니다. 해결: lock 파일을 삭제하세요./data/gateway.*.lock에 있습니다. 하위 디렉터리가 아닙니다.
Config Not Being Read
--allow-unconfigured를 사용하면 Gateway가 최소 config를 만들 수 있습니다. 그 뒤 /data/openclaw.json에 둔 사용자 config는 재시작 시 읽혀야 합니다.
config가 실제로 있는지 확인하세요.
Writing Config via SSH
fly ssh console -C는 shell redirection을 지원하지 않습니다. config 파일을 쓰려면 다음 방법을 사용하세요.
fly sftp가 실패할 수 있습니다. 먼저 삭제하세요.
State Not Persisting
재시작 뒤 자격 증명이나 세션이 사라진다면 상태 디렉터리가 컨테이너 파일시스템에 기록되고 있을 가능성이 큽니다. 해결:fly.toml에 OPENCLAW_STATE_DIR=/data가 설정돼 있는지 확인하고 다시 배포하세요.
업데이트
머신 command 업데이트
전체 재배포 없이 시작 command만 바꾸고 싶다면:fly deploy를 다시 실행하면 머신 command는 fly.toml 값으로 되돌아갈 수 있습니다. 수동 변경을 했다면 배포 후 다시 적용하세요.
비공개 배포(강화형)
기본적으로 Fly는 public IP를 할당하므로 Gateway가https://your-app.fly.dev로 노출됩니다. 편리하지만 인터넷 스캐너(Shodan, Censys 등)에 발견될 수 있습니다.
public 노출 없이 더 안전하게 운영하려면 private 템플릿을 사용하세요.
비공개 배포를 쓰는 경우
- outbound 호출과 메시지만 사용할 때
- webhook callback은 ngrok 또는 Tailscale 터널로 처리할 때
- 브라우저 대신 SSH, proxy, WireGuard로 Gateway에 접근할 때
- 배포 대상을 인터넷 스캐너에 숨기고 싶을 때
설정
기본 config 대신fly.private.toml을 사용합니다.
fly ips list에는 private 타입 IP만 보여야 합니다.
비공개 배포 접근 방법
public URL이 없으므로 다음 중 하나를 사용합니다. 옵션 1: 로컬 프록시(가장 단순함)비공개 배포에서 webhook 사용
public 노출 없이 Twilio, Telnyx 같은 webhook callback이 필요하다면:ngrok tunnel: 컨테이너 내부 또는 sidecar로 ngrok 실행Tailscale Funnel: 특정 경로만 외부에 노출Outbound-only: 일부 제공자(Twilio)는 webhook 없이 outbound 호출만으로도 충분
webhookSecurity.allowedHosts에 public tunnel hostname을 넣으세요.
보안 이점
| Aspect | Public | Private |
|---|---|---|
| Internet scanners | Discoverable | Hidden |
| Direct attacks | Possible | Blocked |
| Control UI access | Browser | Proxy/VPN |
| Webhook delivery | Direct | Via tunnel |
참고
- Fly.io는 x86 아키텍처를 사용합니다. ARM이 아닙니다.
- Dockerfile은 두 아키텍처를 모두 지원합니다.
- WhatsApp/Telegram 온보딩은
fly ssh console에서 진행하세요. - 영구 데이터는
/data볼륨에 저장됩니다. - Signal은 Java와
signal-cli가 필요하므로 커스텀 이미지를 쓰고 메모리는 2GB 이상으로 유지하세요.
비용
권장 구성(shared-cpu-2x, 2GB RAM) 기준:
- 사용량에 따라 월 약
$10-15 - 무료 티어 일부 포함