browser-use 0.11.0をFly.ioでheadful運用したらタイムアウト地獄だった話
はじめに
ブラウザ自動化ライブラリbrowser-useをFly.io上でVNC配信しながら動かそうとしたら、タイムアウトで詰まりました。
browser-useは、AIエージェントがWebブラウザを操作するためのOSSライブラリです。ChatGPTやClaudeなどの大規模言語モデルに「このボタンをクリックして」「このフォームに入力して」といった指示を出すと、browser-useがブラウザを実際に操作してくれます。通常のheadless(画面表示なし)モードではなく、headful(画面表示あり)モードでVNC配信しながらクラウド上で動かすという、少し特殊な構成で動かしています。
同じようなことをやりたい人は少なくないんじゃないかなと思います。私が踏んだ地雷と、そこからどう脱出したかを共有させてください。
この記事を読むと得られること
この記事では、以下の3つの問題について、原因と具体的な解決策を解説します。
- Fly.ioでbrowser-useを起動した際に30秒でタイムアウトする問題
- ページ遷移時に白画面になる、またはDOM(Webページの構造データ)が取得できない問題
- VNC配信環境でブラウザ自動化を安定稼働させるための具体的な設定値
記事を読み終えた後には、Fly.io上でbrowser-useを安定稼働させる設定ファイルを手元に用意できる状態になります。
環境構成
今回の検証で使用した環境は以下の通りです。
| 項目 | 値 | 補足 |
|---|---|---|
| browser-use | 0.11.0 | 2025年12月時点の最新版 |
| デプロイ先 | Fly.io(東京リージョン) | nrt(成田)リージョンを使用 |
| 動作モード | headful | VNC配信のためheadlessは使用不可 |
| VM | shared-cpu-2x / 4GB RAM | 月額約$15程度 |
headfulモードとは、実際にブラウザウィンドウを画面に表示しながら動作するモードです。VNC(Virtual Network Computing、リモートデスクトップ接続の仕組み)で配信するには、画面に表示される実体が必要なため、headlessモード(画面表示なし)は使えません。
発生した症状
症状1: ブラウザ起動がタイムアウトで失敗する
最初にぶつかったのが、ブラウザ起動処理がデフォルトの30秒制限で強制終了される問題でした。エラーメッセージは以下のような内容です。
TimeoutError: Browser launch timed out after 30 seconds
browser-useの内部では、ブラウザ起動に関して2つのイベントが発生します。
| イベント名 | 役割 | デフォルト制限時間 |
|---|---|---|
BrowserStartEvent |
ブラウザプロセスの開始を通知 | 30秒 |
BrowserLaunchEvent |
ブラウザの起動完了を通知 | 30秒 |
原因は、browser-useの内部のタイムアウト設定が30秒になっていることでした。
リクエスト受信
↓
マシン起動(約5〜15秒)
↓
アプリケーション起動(約5〜10秒)
↓
ブラウザ起動(約10〜20秒)
↓
起動完了
flyの環境では、この一連の処理に30秒以上かかるケースがあるため、デフォルトの制限時間では間に合わないのです。ローカル環境では問題なく動いていたので、最初は何が原因か分からず首をかしげていました。
症状2: ページ遷移・DOM取得がタイムアウトで失敗する
ブラウザ起動を乗り越えても、今度は別の壁が待っていました。ページを開こうとすると白画面のまま進まなかったり、以下のようなエラーが出たりします。
TimeoutError: Navigation to URL timed out after 15 seconds
関連するイベントと制限時間は以下の通りです。
| イベント名 | 役割 | デフォルト制限時間 |
|---|---|---|
NavigateToUrlEvent |
指定URLへのページ遷移 | 15秒 |
BrowserStateRequestEvent |
ブラウザの現在状態取得 | 30秒 |
| AXツリー取得 | アクセシビリティツリーの解析 | 10秒 |
AXツリー(アクセシビリティツリー)とは、Webページの構造をツリー形式で表現したデータです。視覚障害者向けのスクリーンリーダーが使う情報と同じもので、「ここにボタンがある」「ここに入力欄がある」といった情報が含まれています。browser-useは、このAXツリーを解析することで操作対象を特定しています。
特にstat.go.jpのような政府系サイトで白画面やDOM取得失敗が頻発しました。政府系サイトはJavaScriptが多く、読み込みに時間がかかる傾向があります。サイト側の問題というより、デフォルト設定の制限時間がクラウド環境には厳しすぎたようです。
解決策
試行錯誤の結果、2つのアプローチで対処できました。環境変数でイベントタイムアウトを延長する方法と、BrowserProfileパラメータでDOM/AXツリー取得の挙動を調整する方法です。
解決策1: 環境変数でイベントタイムアウトを延長する
browser-useは各イベントのタイムアウトを環境変数で上書きできる仕組みを備えています。Fly.ioの設定ファイルfly.tomlに以下を追加しました。
[env]
# Browser-use watchdog timeout overrides (seconds)
TIMEOUT_BrowserStartEvent = "90"
TIMEOUT_BrowserLaunchEvent = "90"
Fly.ioのコールドスタートを考慮して、デフォルト30秒から90秒に延長しています。少し余裕を持たせすぎかもしれませんが、タイムアウトで落ちるよりはマシです。
環境変数名の命名規則はTIMEOUT_{イベント名}です。browser-useの内部で使われているイベント名をそのまま指定できます。設定可能なイベント一覧は、browser-useのソースコード内events.pyで確認できます。
どの値を設定すべきか判断する方法
適切な値は環境によって異なります。以下の手順で調整してください。
- まずデフォルト値の2倍(60秒)で試す
- タイムアウトが発生したら、ログでどのイベントが失敗したか確認する
- 該当イベントの値を30秒ずつ増やして再試行
- 安定したら、その値に10〜20%のバッファを追加
私の環境では、90秒で安定しました。ただし、マシンスペックをshared-cpu-1xからshared-cpu-2xに上げると、60秒でも動作する場合があります。
解決策2: BrowserProfileでDOM/AXツリー取得を調整する
DOM/AXツリー取得のタイムアウトは、BrowserProfileのパラメータで緩和できます。BrowserProfileはブラウザの動作設定をまとめたオブジェクトで、待機時間やiframe(ページ内に埋め込まれた別のWebページ)の取得制限などを細かく制御できます。
「速度」と「安定性」のトレードオフを自分で調整できるのが、このライブラリの面白いところです。
from browser_use import BrowserProfile
browser_profile = BrowserProfile(
# 広告・クッキーバナーを自動ブロックする拡張機能を有効化
enable_default_extensions=True,
# iframeの取得制限
max_iframes=60,
max_iframe_depth=3,
# ページ読み込み完了の判定を緩やかに設定
minimum_wait_page_load_time=1.5,
wait_for_network_idle_page_load_time=1.0,
# アクション間の待機時間を広げて処理の負荷を分散
wait_between_actions=0.5,
)
各パラメータの設定意図
各パラメータの役割と、なぜその値を選んだのかを説明します。
enable_default_extensions(拡張機能の有効化)のデフォルト値はTrueです。uBlock Origin(広告ブロック)、Cookie自動承認、ClearURLs(トラッキングパラメータ除去)等の拡張機能が有効になります。初回起動は5〜10秒遅くなりますが、広告やクッキーバナーによる操作の妨げを減らせます。広告が多いサイトを操作する場合は有効にしておくことを推奨します。
max_iframesとmax_iframe_depth(iframe制限)のデフォルト値はmax_iframes=100、max_iframe_depth=5です。私は60と3に設定しました。iframeとは、ページ内に埋め込まれた別のWebページのことです。広告や解析ツールがiframeで埋め込まれていることが多く、全てのiframeを解析すると処理時間が増大します。ただし、61個目以降のiframeに含まれるコンテンツは取得対象外になります。iframe内にメインコンテンツがあるサイト(一部の企業サイトや古いWebアプリケーション)では注意してください。
minimum_wait_page_load_timeとwait_for_network_idle_page_load_time(ページ読み込み待機)のデフォルト値は0.25秒と0.5秒です。私は1.5秒と1.0秒に設定しました。ページのDOM構築が完了してから、追加のJavaScriptが実行されるまでに若干のラグがあります。デフォルト値だと、DOMは構築されたがJavaScriptの実行が完了していない状態でAXツリーを取得しようとして失敗することがありました。待機時間を延ばすことで、白画面問題がほぼ解消しました。
wait_between_actions(アクション間待機)のデフォルト値は0.1秒です。私は0.5秒に設定しました。クリックや入力などの操作間隔を広げると、ブラウザ側の処理が追いつきやすくなります。10ページを順番に処理する場合、この変更だけで約4秒(0.4秒 x 10アクション)余分にかかりますが、途中でコケる確率が下がります。
設定値の早見表
| パラメータ | デフォルト | 推奨値 | 変更理由 |
|---|---|---|---|
| enable_default_extensions | True | True | ポップアップ自動処理 |
| max_iframes | 100 | 60 | 解析負荷を40%削減 |
| max_iframe_depth | 5 | 3 | 深い階層は広告が多い |
| minimum_wait_page_load_time | 0.25秒 | 1.5秒 | 白画面防止 |
| wait_for_network_idle_page_load_time | 0.5秒 | 1.0秒 | JS実行完了を待つ |
| wait_between_actions | 0.1秒 | 0.5秒 | 安定性向上 |
この設定の考え方を一言で表すなら、「急がば回れ」でしょうか。待機時間を増やすことで全体の処理時間は伸びますが、途中でコケるよりはずっとマシです。
トレードオフの整理
この設定には明確なトレードオフがあるので、正直に書いておきます。用途によって適切な設定は変わるため、自分のユースケースに合わせて調整してください。
処理速度の低下について
待機時間を増やしているため、全体の処理時間が伸びます。具体的には以下の影響があります。
- 1ページあたり約2〜3秒の追加待機
- 10ページ処理で約20〜30秒の増加
- 100ページ処理で約3〜5分の増加
大量のページを処理するバッチ処理では、この差が積み重なります。処理速度を優先する場合は、minimum_wait_page_load_timeを1.0秒程度まで下げることを検討してください。
iframe内コンテンツの取りこぼしについて
max_iframesを60に絞ることで、61個目以降のiframeは取得対象外になります。以下のケースでは注意が必要です。
- iframe内にメインコンテンツがあるサイト
- 古いWebアプリケーション(フレームセットを使用)
- 複雑な管理画面(ダッシュボードにiframeを多用)
事前にブラウザの開発者ツール(F12キー)でElements > iframeタグを検索し、対象サイトのiframe数を確認しておくと安心です。
私の場合は「確実に動く」を優先したので、この設定に落ち着きました。
まだ詰まる場合の追加対策
上記設定でも不安定な場合は、以下を試してみてください。私も最初は上の設定だけでは安定しないケースがありました。
ページ遷移タイムアウトの延長
重いサイトや海外サーバーのサイトでは、ページ遷移に時間がかかります。
[env]
TIMEOUT_NavigateToUrlEvent = "60"
60〜90秒に設定すると改善することが多いです。
ブラウザ状態取得タイムアウトの延長
DOM要素が大量にあるサイト(ECサイトの商品一覧ページなど)では、状態取得に時間がかかります。
[env]
TIMEOUT_BrowserStateRequestEvent = "90"
90〜120秒に設定すると改善することが多いです。
設定ファイルの完全版
最後に、今回紹介した設定をまとめた完全版を掲載します。コピーして使ってください。
fly.toml
[env]
TIMEOUT_BrowserStartEvent = "90"
TIMEOUT_BrowserLaunchEvent = "90"
TIMEOUT_NavigateToUrlEvent = "60"
TIMEOUT_BrowserStateRequestEvent = "90"
Python設定
from browser_use import BrowserProfile
browser_profile = BrowserProfile(
enable_default_extensions=True,
max_iframes=60,
max_iframe_depth=3,
minimum_wait_page_load_time=1.5,
wait_for_network_idle_page_load_time=1.0,
wait_between_actions=0.5,
)
まとめ
browser-useをFly.ioでheadful運用する際のポイントは、結局のところ「待機時間とのバランス」に尽きるな、という印象です。
安定稼働のために押さえるべきポイントは3つあります。
1つ目は、環境変数でのイベントタイムアウト延長です。TIMEOUT_BrowserStartEvent等でブラウザ起動の制限時間を90秒に延長することで、Fly.ioのコールドスタートに対応できます。
2つ目は、iframe取得の制限です。max_iframesとmax_iframe_depthでAXツリー解析の負荷を軽減することで、DOM取得の安定性が向上します。
3つ目は、ページ読み込み待機の延長です。minimum_wait_page_load_time等でJavaScriptの実行完了を待つことで、白画面を防止できます。
browser-useはイベントタイムアウトを環境変数で簡単に上書きでき、BrowserProfileでDOM/ロード周りを細かく制御できるため、再現性の高いチューニングがしやすいライブラリだと感じました。
headful + VNC配信という少し特殊な環境でも、パラメータ調整で安定稼働を実現できています。同じような構成で苦労している方の参考になれば嬉しいです。
参考
About me
現在、市場調査やデスクリサーチの生成AIエージェントを作っています 仲間探し中 / Founder of AI Desk Research Agent @deskrex , https://deskrex.ai
ぜひお気軽にチャットしましょう!
お仕事のご相談は以下まで、AIエージェントの開発や研修、調査代行やビジネスコンサルなどの対応も可能です。
生成AIデスクリサーチサービス Deskrex | サービスページ
生成AIデスクリサーチエージェント Deskrex App | アプリケーションサイト
DeskrexAIリサーチ | メディア
株式会社Deskrex | 会社概要
Deskrex | Xページ
- 会社概要:https://www.deskrex.ai/
- Deskrex App:https://app.deskrex.ai/
- サービスページ:https://lp.deskrex.ai/
- メディア:https://media.deskrex.ai/
- X:https://x.com/deskrex
Discussion