Obsidian Vaultを「個人ナレッジ基盤」に変える——Cloudflare R2 + mkdocs + Pages + Access構成ガイド

導入の目的

Obsidianは素晴らしいツールです。Markdownベースでデータが手元に残り、プラグインエコシステムも充実しています。自分も日常的にObsidianでメモ、調査ログ、技術ノートを書き溜めています。また、GitHub上のPrivateリポジトリで管理することで、多数のデバイスやスマートフォンでの同期も可能で便利。

ただ、画像を含むVaultの容量が大きくなり同期も遅くなってきた。スマートフォンの容量もかなり食う。 公式の同期の仕組もあるがどうやら容量制限があるらしい。

自分だけが見られるWebサイトでVaultの内容が公開できればいいのだが…と考えて、現時点でたどりつけたのがCloudflareを活用した構成です。この記事では、自分が実際に運用している構成を、セットアップ手順とともに解説します。

構成の全体像 自分だけ見られるVaultを目指して

自分がやっていることを、「自分だけが見られるVault」と定義します。単なる静的サイトホスティングではなく、以下の要件を満たすシステムです。

  • 編集体験: Obsidianなりエディタなりローカルでできればよい
  • 画像管理: クラウドストレージに自動アップロード
  • 公開: Markdown → 静的サイトに自動ビルド&デプロイ
  • 認証: 自分だけがアクセスできる
  • コスト: サービス利用にかかるコストは低減、自分の学習にかかるコストは「私が楽しければ無視」してよい

以下がアーキテクチャの全体像です。

graph TD
    subgraph Local["🖥 ローカル環境"]
        Obsidian["Obsidian<br/>(Markdown編集)"]
        Git["Git"]
    end

    subgraph GitHub["GitHub"]
        Repo["GitHubリポジトリ"]
    end

    subgraph Build["ビルド"]
        MkDocs["mkdocs build<br/>(Material for MkDocs)"]
    end

    subgraph Cloudflare["Cloudflare"]
        Pages["Cloudflare Pages<br/>(ホスティング)"]
        R2["Cloudflare R2<br/>(画像ストレージ)"]
        CDN["Cloudflare CDN"]
        Access["Cloudflare Access<br/>(Zero Trust認証)"]
    end

    Viewer["👤 閲覧者(自分のみ)"]

    Obsidian -->|"Markdown保存"| Git
    Git -->|"git push"| Repo
    Repo -->|"Webhook トリガー"| MkDocs
    MkDocs -->|"静的サイト出力"| Pages
    Pages --> CDN

    Obsidian -->|"ローカル保存"| LocalImg["ローカル画像<br/>(_resources)"]
    LocalImg -->|"Wranglerスクリプト<br/>一括アップロード"| R2
    R2 -->|"画像配信"| CDN

    CDN --> Access
    Access -->|"認証済みアクセス"| Viewer

    style Local fill:#e8f4f8,stroke:#2196F3,stroke-width:2px
    style GitHub fill:#f0f0f0,stroke:#333,stroke-width:2px
    style Build fill:#fff3e0,stroke:#FF9800,stroke-width:2px
    style Cloudflare fill:#fff8e1,stroke:#F57C00,stroke-width:2px
    style Viewer fill:#e8f5e9,stroke:#4CAF50,stroke-width:2px

この構成のポイントは、すべてのインフラがCloudflareの無料枠に収まることです。各構成要素を順番に見ていきます。

構成要素1 Cloudflare R2(画像ストレージ)

なぜR2を選んだのか

Obsidianで技術ノートを書いていると、スクリーンショットや図を大量に貼り付けます。これらをGitリポジトリに含めると、リポジトリが肥大化してcloneやpushが遅くなります。

Cloudflare R2を画像ストレージとして使う理由は明確です。

  • エグレス(転送量)課金が無料: AWS S3と違い、データ取り出しに費用がかかりません
  • 無料枠が十分: ストレージ10GB/月、ClassA操作100万回/月、ClassB操作1,000万回/月
  • S3互換API: 既存のツールやプラグインがそのまま使えます

今の自分の用途であれば、10GBの無料枠を超えることはないと判断しました。 まあ、多少超えても、料金はかなりお安いですしね…

WranglerスクリプトとPythonによる一括アップロード

ObsidianからR2へ画像を直接アップロードするプラグイン(S3 Image Uploaderなど)を使う手もありますが、自分の構成ではよりシンプルに「ローカルに画像を保存し、自作スクリプトで一括アップロード・リンク置換する」アプローチをとっています。

これにより、Obsidian上では通常のローカルファイルとして画像を扱いながら、Web公開時はR2の画像を配信できるようになります。

R2バケットの作成

  1. Cloudflareダッシュボードにログイン
  2. 「R2 Object Storage」→「バケットを作成」を選択
  3. バケット名(例: obsidian-img)を入力し、リージョンを選択

[!info] 📸 スクリーンショット: R2バケット作成画面 ここにR2バケット作成画面のスクリーンショットを挿入してください。 撮影ポイント: バケット名の入力欄とリージョン選択が見えるように撮影

Wrangler CLIによる一括アップロード

Cloudflareの公式CLIツールであるwranglerを使用して、Obsidianの画像フォルダ(3_Resources/_resourcesなど)にある画像をR2に一括アップロードします。

自分は以下のようなBashスクリプトを用意して運用しています。

# 抜粋: _resources内の画像を全てR2にアップロードする
while IFS= read -r -d '' filepath; do
    rel_path="${filepath#$RESOURCES_DIR/}"
    r2_key="_resources/$rel_path"
    content_type=$(get_content_type "$filepath")

    wrangler r2 object put "$BUCKET/$r2_key" \
        --file="$filepath" \
        --content-type="$content_type" \
        --remote
done < <(find "$RESOURCES_DIR" -type f \( -name "*.png" -o -name "*.jpg" \) -print0)

[!info] 📸 スクリーンショット: Wranglerによるアップロード実行ログ ここにWranglerスクリプト実行時のターミナル画面のスクリーンショットを挿入してください。

Markdown内のリンクをR2のURLに置換

画像がR2にアップロードできたら、Markdown内のローカル画像リンクを、R2の公開URL(カスタムドメイン)へ書き換える必要があります。

これもPythonスクリプトを用意し、正規表現を用いてMarkdownファイル内のリンクを一括置換しています。

# 抜粋: Pythonによるリンク書き換え処理
import re
LINK_PATTERN = re.compile(r'(\!\[[^\]]*\]\()((?:\.\.\/)*)(?:3_Resources\/)?(_resources\/)([^)\s]+)(\))')
# ... リンクを https://<カスタムドメイン>/_resources/xxx に置換 ...

構成要素2 Git + mkdocs + Cloudflare Pages(ビルド&ホスティング)

Markdown → 静的サイトの自動パイプライン

この構成の中核は、ObsidianのMarkdownをGitで管理し、mkdocsで静的サイトにビルドし、Cloudflare Pagesで自動デプロイするパイプラインです。

Material for MkDocsを使う理由は、Obsidianとの互換性が高く、見た目も洗練されているためです。WikiリンクやCallout記法への対応もプラグインで実現できます。

デプロイフロー

以下がデプロイの流れです。Markdownを保存してpushするだけで、自動的にサイトが更新されます。

sequenceDiagram
    actor User as ユーザー
    participant Obs as Obsidian
    participant Git as Git(ローカル)
    participant GH as GitHub
    participant MkD as MkDocs (Material)
    participant Pages as Cloudflare Pages
    participant R2 as Cloudflare R2
    participant CDN as Cloudflare CDN
    participant Auth as CF Access (Zero Trust)
    actor Viewer as 閲覧者

    Note over User,Viewer: Markdownコンテンツのデプロイフロー

    User->>Obs: Markdownを編集・保存
    Obs->>Git: git add / git commit
    Git->>GH: git push

    Note over GH,Pages: 自動ビルド・デプロイ

    GH->>Pages: Webhook: pushイベント通知
    Pages->>GH: ソースコード取得
    Pages->>MkD: mkdocs build 実行
    MkD-->>Pages: 静的HTML生成完了
    Pages->>CDN: ビルド成果物をデプロイ

    Note over Obs,R2: 画像アップロード・置換フロー(バッチ処理)

    User->>Obs: 画像を貼り付け(ローカル保存)
    User->>User: R2アップロード・リンク置換<br/>スクリプトを実行
    User->>R2: Wranglerで画像をR2へ一括アップロード
    User->>Obs: MarkdownのリンクをR2のURLに一括置換
    R2->>CDN: 画像をCDN経由で配信

    Note over CDN,Viewer: 認証付きアクセス

    Viewer->>CDN: サイトにアクセス
    CDN->>Auth: 認証チェック
    Auth-->>Viewer: 認証画面を表示
    Viewer->>Auth: 認証情報を入力
    Auth-->>CDN: 認証OK
    CDN-->>Viewer: サイトコンテンツを表示

mkdocs.ymlの設定例

以下は自分が使っている mkdocs.yml の設定例です。Material for MkDocsをベースに、Obsidianとの互換性を高める設定を入れています。

site_name: My Obsidian Notes
site_dir: site
use_directory_urls: false

# Obsidian特有の不要なファイルをビルド対象から外す設定
exclude_docs: |
  .*
  Excalidraw/**
  4_Archives/PDF/**
  3_Resources/Logs/**
  3_Resources/images/**
  3_Resources/Prompts/**
  3_Resources/Scripts/**
  3_Resources/Snippets/**
  3_Resources/Templates/**
  3_Resources/VRM/**
  *.py
  GEMINI.md
  CLAUDE.md
  README.md

theme:
  name: material
  language: ja
  custom_dir: overrides
  features:
    - navigation.instant   # ページ遷移を高速化(SPA風)
    - navigation.tracking  # 読んでいる場所を追跡
    - navigation.sections  # PARA構造を強調
    - navigation.top       # 長いノート用「トップへ戻る」ボタン
  palette:
    # ライトモード(昼用)の設定
    - scheme: default
      primary: indigo
      accent: purple
      toggle:
        icon: material/brightness-7
        name: Switch to dark mode
    # ダークモード(夜用)の設定
    - scheme: slate
      primary: indigo
      accent: lime
      toggle:
        icon: material/brightness-4
        name: Switch to light mode
extra_css:
  - stylesheets/extra.css

markdown_extensions:
  - obsidian_callouts
  - admonition
  - pymdownx.magiclink
  - pymdownx.tilde
  - pymdownx.superfences:
      custom_fences:
        - name: mermaid
          class: mermaid
          format: !!python/name:pymdownx.superfences.fence_code_format
  - pymdownx.tasklist:
      custom_checkbox: true
  - footnotes

extra_javascript:
  - https://unpkg.com/mermaid@11/dist/mermaid.min.js

plugins:
  - search:
      enabled: false
  - obsidian-bridge      # Wikiリンク等をサポート

Cloudflare Pagesの設定

  1. Cloudflareダッシュボードで「Workers & Pages」→「新規作成」→「Pagesタブ」を選択
  2. GitHubリポジトリを接続
  3. ビルド設定を入力:
    • ビルドコマンド: bash build.sh
    • ビルド出力ディレクトリ: site
    • 環境変数: PYTHON_VERSION = 3.12

※自分の環境では、Obsidian用の不要なディレクトリを除外したり、特定プラグインのバグ回避パッチを当てるために build.sh というシェルスクリプトを用意し、その中で mkdocs build を呼び出しています。

[!info] 📸 スクリーンショット: Cloudflare Pagesのビルド設定画面 ここにCloudflare Pagesのビルド設定画面のスクリーンショットを挿入してください。 撮影ポイント: ビルドコマンド、出力ディレクトリ、環境変数の設定が見えるように撮影

requirements.txt には以下のように、使用するテーマやプラグインを記載します。

mkdocs==1.6.1
mkdocs-material==9.7.2
git+https://github.com/tadashi-aikawa/mkdocs-obsidian-bridge

これでGitHubにpushするたびに、Cloudflare Pages上で自動的にビルドとデプロイが実行されます。無料枠でもビルド500回/月、帯域幅無制限と、個人利用には十分すぎるスペックです。

[!info] 📸 スクリーンショット: Cloudflare Pagesのデプロイ履歴画面 ここにCloudflare Pagesのデプロイ履歴画面のスクリーンショットを挿入してください。 撮影ポイント: デプロイのステータス(成功/失敗)とビルド時間が見えるように撮影

構成要素3 Cloudflare Access(認証)

Zero Trustで「自分だけ」のアクセス制御

サイトを公開したら、次は「自分だけがアクセスできるようにする」必要があります。ここで使うのがCloudflare Access(Zero Trust)です。

認証方式の選択

Cloudflare Accessでは複数の認証方式を選択できます。

  • メールOTP(ワンタイムパスワード): 設定不要で最も手軽。指定したメールアドレスにコードが届きます
  • Google認証: Googleアカウントでのログイン。OAuthの設定が必要ですが、普段使いには便利です
  • GitHub認証: GitHubアカウントでのログイン。開発者なら自然な選択肢です

自分はメールOTPとGitHub認証を併用していますが、以下メールOTPでの設定手順を記載します。

Cloudflare Accessの設定手順

  1. Cloudflare Zero Trustダッシュボード(one.dash.cloudflare.com)にアクセス
  2. 「Access」→「Applications」→「Add an application」を選択
  3. 「Self-hosted」を選択
  4. アプリケーションドメインにCloudflare PagesのURLを入力
  5. ポリシーを作成:
    • Action: Allow
    • Include: Emails — 自分のメールアドレスを指定

[!info] 📸 スクリーンショット: Cloudflare Accessのアプリケーション設定画面 ここにCloudflare Accessのアプリケーション設定画面のスクリーンショットを挿入してください。 撮影ポイント: アプリケーションドメインとポリシー設定(Allow + メールアドレス指定)が見えるように撮影

[!info] 📸 スクリーンショット: Cloudflare Accessの認証画面(ユーザー視点) ここにCloudflare Accessの認証画面のスクリーンショットを挿入してください。 撮影ポイント: メールアドレス入力画面またはOTP入力画面

これで、サイトにアクセスすると認証画面が表示され、許可されたメールアドレスでのみアクセスできるようになります。

コスト比較——Obsidian Publish vs この構成

この構成の最大の魅力は、「Cloudflareの無料枠だけで完結する」ことです。

項目 Obsidian Publish この構成(Cloudflare)
ホスティング $8/月(Publish) $0(Pages無料枠: ビルド500回/月、帯域幅無制限)
画像ストレージ Publish内に含まれる $0(R2: 10GB/月無料枠)
認証・アクセス制御 なし(公開 or リンク共有) $0(Access: 50ユーザー無料枠)
CDN Publish内に含まれる $0(Cloudflare CDN)
カスタムドメイン 対応(追加設定) 対応(Pages標準機能)
月額合計 $8/月(約$96/年) $0/月

[!note] 料金に関する注記 ※執筆時点(2026年2月)の情報です。最新の料金は各公式サイトをご確認ください。

もちろん、Obsidian Publishには「Obsidianから直接公開できる手軽さ」「グラフビューの公開」といったObsidianならではの価値があります。

まとめ

この構成の利点

  • コスト$0: Cloudflareの無料枠だけで運用可能
  • 編集体験が変わらない: ローカル環境での執筆フローはそのまま
  • 自由度が高い: Material for MkDocsのテーマ・プラグインで見た目を自在にカスタマイズ
  • セキュリティ: Zero Trustでエンタープライズグレードのアクセス制御
  • データの所有: すべてのデータが自分のGitリポジトリとCloudflareアカウントにある

注意点

  • セットアップの手間: 初回構築にはCloudflareの各サービスの設定が必要です。慣れていない方は数時間かかる可能性があります
  • mkdocsの学習コスト: mkdocs.yml の設定やプラグインの理解に多少の学習が必要です
  • Obsidianとの完全互換ではない: 一部のObsidian独自記法(埋め込みやDataview等)はmkdocsでは再現できません。公開用のノートはmkdocs互換の記法を意識する必要があります

まだ改善の余地はあるかもしれませんが、ひとまず当初の目的を達成できました。なによりこういう仕組みをという体験には、コスト以上の満足感があります。Obsidianのローカルファースト思想と、Cloudflareの無料インフラを組み合わせることで、個人ナレッジ基盤は驚くほど手軽に構築できます。

ぜひ試してみてください。

参考資料