CI が落ちる。ログを見ても分からない。「この画面、実際に目で見られたらすぐ分かるのに」と思ったことはないでしょうか。
github-actions-desktop は、その願いを YAML 20行で叶えてしまうリポジトリです。Fork してワークフローを実行すると、ログに https://xxxxx.trycloudflare.com という URL が出てきます。開くと、そこにブラウザで操作できる Linux デスクトップがあります。Chromium も動きます。
TL;DR
- Webtop(Docker)+ Cloudflare Tunnel を GitHub Actions 上で起動するだけ
- パブリックリポジトリなら 4 vCPU / 16GB RAM が無料、ただし 6時間で強制終了
- 先月GAしたばかりの
background: trueがきれいにハマっている - デフォルトは認証なし。URLを知られた時点で root を取られます(対策は後述)
- 規約上は検証用。常用サーバーにしてはいけません
3分で試す
- リポジトリを Fork
- Actions タブ →
Desktopワークフロー → Run workflow - 2つ目のステップのログに出る
trycloudflare.comの URL を開く
URL が出た直後は 502 が返ることがあります。Docker イメージの pull がまだ終わっていないだけなので、30秒ほど待ってリロードしてください。
これが全部です
name: Desktop
on:
workflow_dispatch:
jobs:
desktop:
runs-on: ubuntu-latest
steps:
- name: Start Docker
background: true
run: |
docker run -d \
--network host \
--security-opt seccomp=unconfined \
--shm-size=1gb \
--restart unless-stopped \
ghcr.io/linuxserver/webtop
- name: Desktop URL https://<random-subdomain>.trycloudflare.com
run: |
wget -q https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
sudo dpkg -i cloudflared-linux-amd64.deb >/dev/null 2>&1
cloudflared tunnel --url http://localhost:3000
ステップ名に URL の形式を書いておいて、ログを見なくても何が起きるか分かるようにしているのが地味に上手いところです。
なぜこれで動くのか
短いコードほど、裏で効いている前提が多いものです。4つに分けて見ていきます。
1. background: true(2026-06-25 GA)
同じジョブ内のステップを並列に走らせる機能が、6月に正式リリースされました。background: true を付けたステップは起動と同時に制御を次へ返し、wait: や cancel: で後から待機・停止を制御できます。
正直に書くと、ここでは docker run -d がもともと即座に返るので、background: true がなくても動きます。効いているのは イメージ pull の時間で、その裏で cloudflared のダウンロードとインストールが進みます。数十秒の短縮ですが、「重い常駐プロセスをステップとして分離しつつ、次に進む」という書き方が YAML の見た目どおりに素直に書けるようになったのは大きい。従来なら nohup ... & と sleep で誤魔化していた部分です。
2. Webtop(Selkies)
linuxserver/webtop は、フルデスクトップ環境をブラウザから使えるようにしたコンテナ群です。2025年6月に基盤が KasmVNC から Selkies に移行し、WebRTC ベースのストリーミングになりました。VNC のようなカクつきがほとんどありません。
ここで一点、README の説明と実際が少しずれています。latest タグは Alpine + XFCE です。Ubuntu が欲しい場合は明示的に ghcr.io/linuxserver/webtop:ubuntu-xfce を指定してください。なお Alpine 版のブラウザは 2024年から Firefox ではなく Chromium になっています。
3. Cloudflare Quick Tunnel
cloudflared tunnel --url http://localhost:3000 は、アカウントもドメインも要らずにランダムなサブドメインを払い出してくれるモードです。GitHub Actions のランナーは外部から直接叩けないので、内側から張るトンネルが唯一の出口になります。
ここが一番おもしろい部分です。 Webtop はポート 3000 が HTTP、3001 が HTTPS なのですが、公式ドキュメントは 3000 について「リバースプロキシ経由で使うこと」と書いています。理由は、映像・音声に使う WebCodecs などのブラウザ機能が、セキュアコンテキスト(HTTPS)でないと動かないからです。
そして Cloudflare Tunnel は、エッジ側で HTTPS を終端して https://xxxxx.trycloudflare.com として配信します。つまり cloudflared がそのリバースプロキシそのものになっている。3001 側に繋ぐと自己署名証明書のせいで --no-tls-verify が必要になるので、3000 を選んでいるのは正解です。20行の中で一番よく考えられている行だと思います。
4. 3つのフラグ
| フラグ | 役割 |
|---|---|
--network host |
ポートマッピングを省略してランナーのポート3000を直接使う |
--shm-size=1gb |
共有メモリ不足でブラウザが落ちるのを防ぐ。デスクトップ系イメージでは公式推奨 |
--security-opt seccomp=unconfined |
一部の GUI アプリが必要とするシステムコールを通す |
なお --restart unless-stopped は、ジョブごと消えるランナーの上では実質的に何もしていません。ローカル用の docker run をそのまま持ってきた名残でしょう。
スペックと制限
| 項目 | 値 |
|---|---|
| CPU / RAM | 4 vCPU / 16 GB(パブリックリポジトリの ubuntu-latest) |
| ディスク | 十数 GB 程度 |
| 1ジョブの上限 | 6時間(超過で強制キャンセル、延長不可) |
| 料金 | パブリックリポジトリなら無料・無制限 |
無料で 16GB RAM のデスクトップが手に入るのは率直にすごい。ただし 6時間で必ず消えますし、ファイルも一緒に消えます。
罠:そのままだと誰でも root を取れます
ここは飛ばさないでください。
Webtop は デフォルトで認証がありません。そしてブラウザ画面のサイドバーには、パスワードなしで sudo が通るターミナルが付いています。
つまり、trycloudflare.com の URL を知っている人は誰でも、あなたのランナー上で root シェルを取れます。ランナーには GITHUB_TOKEN を含む環境変数が乗っています。URL は推測困難ですが、Actions のログを見られる状況(パブリックリポジトリならログは誰でも見られます)なら話は別です。
最低限、Basic 認証をかけてください。
- name: Start Docker
background: true
run: |
docker run -d \
--network host \
--security-opt seccomp=unconfined \
--shm-size=1gb \
-e CUSTOM_USER=admin \
-e PASSWORD=${{ secrets.DESKTOP_PASSWORD }} \
-e LC_ALL=ja_JP.UTF-8 \
ghcr.io/linuxserver/webtop:ubuntu-xfce
ついでに LC_ALL=ja_JP.UTF-8 で日本語ロケールにしています。さらに固めたい場合は HARDEN_DESKTOP=true で sudo・ターミナル・ファイル転送をまとめて封じられます。
timeout-minutes でジョブ自体に短い上限を付けておくのも有効です。放置して6時間フルに走らせる理由はありません。
jobs:
desktop:
runs-on: ubuntu-latest
timeout-minutes: 30
規約の話(重要)
リポジトリの README にも「検証・確認目的のみ」と書かれています。これは謙遜ではなく、GitHub の Additional Product Terms に根拠があります。
Actions の禁止用途として、暗号通貨マイニング、サーバーレスコンピューティング、商用サービスとしての提供、そして最後に「そのリポジトリのソフトウェアプロジェクトの production, testing, deployment, or publication に関係しない一切の活動」が挙げられています。違反すると、ジョブの停止・リポジトリの無効化・アカウント停止まであり得ます。
なので、こういう使い方は筋が通ります。
- Playwright / Selenium の E2E が CI でだけ落ちるとき、その場でブラウザを目視デバッグする
- ヘッドレスでは再現しないレンダリング崩れを確認する
- 「ローカルでは動くんですけど」を潰す
逆に、これは明確にアウトです。
- 常用の作業マシンとして使う
- ダウンロード・トレント・マイニングの踏み台にする
- 6時間ごとに自動で再起動して永続サーバーにする
無料の VPS ではありません。CI をデバッグするための GUI です。この線引きさえ守れば、かなり便利な道具だと思います。
まとめ
- YAML 20行で、ブラウザから触れる Linux デスクトップが GitHub Actions 上に立つ
-
background: true、Selkies の WebRTC、Cloudflare Tunnel による HTTPS 終端が噛み合っている - 認証なしがデフォルトなので
CUSTOM_USER/PASSWORDは必ず付ける - 用途は CI のデバッグ。常用サーバーにはしない
短いコードほど、読むと学びが多い。このリポジトリはその好例でした。