roji は既定ではループバックアドレスでしか待ち受けない( 設定ガイドの bind を参照)。外から開発機に届かせたい 場面(webhook の受信、作りかけのアプリを人に見せる)のために、Cloudflare Tunnel を通して選んだルートだけを公開できる。

bind を広げるのとは違い、公開の単位はコンテナごとで、ダッシュボードと その API は公開されない。

必要なもの

  • Cloudflare アカウントと、そこに登録済みのゾーン(example.com など)
  • cloudflared が PATH にあること。roji は cloudflared を子プロセスとして実行する
cloudflared tunnel login          # ブラウザが開き ~/.cloudflared/cert.pem を作る
cloudflared tunnel create roji    # named tunnel を作る

設定

~/.config/roji/config.yamltunnel: ブロックを足す。

tunnel:
  domain: example.com   # Cloudflare 上のゾーン
  name: roji            # cloudflared tunnel create で作った named tunnel
  port: 8080            # トンネル専用リスナー(127.0.0.1 固定、既定 8080)
  auto_start: false     # roji が cloudflared を起動するか

domainname の両方が揃ったときだけトンネルが有効になる。環境変数は 無く、設定ファイル専用。

auto_start: true にすると roji が cloudflared を起動し、roji の終了時に 停止する。false のままなら、リスナーだけが開くので cloudflared tunnel run roji を自分で回す。

公開するルートを選ぶ

コンテナに roji.tunnel ラベルを付ける。付けたものだけが外に出る。

# docker-compose.yml
services:
  api:
    labels:
      - "roji.tunnel=true"

true / 1 / yes / on が肯定として扱われ、それ以外(ラベルなし、空、 書き間違い)はすべて非公開になる。roji はネットワーク上のコンテナをラベル 無しで拾うので、既定で公開すると全部が外に出てしまう。だから明示的な オプトインにしてある。

DNS レコード

*.{domain} をトンネルに向ける設定は手動で行う。roji に Cloudflare の API トークンを持たせない方針のため、この 1 手だけは roji がやらない。

cloudflared tunnel route dns roji "*.example.com"

Cloudflare のダッシュボードから <uuid>.cfargotunnel.com への CNAME を 作っても同じ。

ドメインは 1 階層に収める

Cloudflare の Universal SSL がカバーするのは example.com*.example.com だけ。tunnel.example.com のような 2 階層の名前にすると *.tunnel.example.com の証明書が必要になり、有償の Advanced Certificate Manager が要る。設定バリデーションが警告を出す。

ホスト名の対応

ローカル名の接尾辞を入れ替えたものが公開名になる。

ローカル公開
web.dev.localhostweb.example.com
api.dev.localhostapi.example.com

バックエンドに渡される Host はローカル名のまま。開発サーバーの許可 ホスト設定(Vite の server.allowedHosts など)は、いつもどおり *.dev.localhost を書いておけばよい。

公開されないもの

トンネル経由のリクエストは、通常のリスナーとは別のポートに届く。 cloudflared は 127.0.0.1 から接続してくるので、リクエストの中身では ローカルのブラウザと区別がつかない。区別できるのは到達したポートだけで、 その分離を使って以下を落としている。

  • /_api/*/_assets/*。パスだけで無条件に 404 になり、設定では 開けられない。/_api/projects/{name}/up は無認証でコンテナを起動でき、 /_api/logs はリクエストログを読める
  • ダッシュボードのホスト名とベースドメイン
  • roji.tunnel の無い Docker ルート
  • 静的サイト(static_sites: にオプトインの綴りが無いため、現状すべて非公開)

拒否はどの理由でも同じ素の 404 を返す。理由ごとに文言を分けると、どの ホスト名が存在するかを外に教えてしまうため。

確認する

roji doctor

cloudflared の導入、ログイン状態、named tunnel の存在を確認する。DNS レコードだけは roji から検証できないので、案内の表示に留まる。

ダッシュボードでは、公開中のルートに 🌐 バッジと公開 URL が出る。ラベルを 付けても tunnel: を設定していなければ何も公開されないので、バッジも 出ない。

入れていないもの

  • roji tunnel start/stop/status コマンド。auto_start: false のときは cloudflared tunnel run を自分で回す前提
  • 静的サイトの公開
  • cloudflared がクラッシュした後の自動再起動。roji はプロセスマネージャでは ないので、落ちたことをログに出して放置する。認証に失敗し続けるトンネルが 黙って再試行を繰り返す方が困るため
  • Cloudflare API トークンによる DNS レコードの自動作成