1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitHub Actions が「ブラウザから触れる Linux デスクトップ」に変わる

1
Last updated at Posted at 2026-08-29

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分で試す

  1. リポジトリを Fork
  2. Actions タブ → Desktop ワークフロー → Run workflow
  3. 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 のデバッグ。常用サーバーにはしない

短いコードほど、読むと学びが多い。このリポジトリはその好例でした。

参考

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?