VPS Notes
セルフホスト

n8n を VPS にセルフホストして、Claude と MCP で繋ぐ (Docker Compose + Tailscale)

自動化ツール n8n を月 2,000 円台の VPS に Docker で立て、Tailscale 経由で HTTPS のままスマホから使えるようにします。Claude を挟んだワークフローの作り方と、n8n を MCP サーバーにして Claude Code から呼ぶ手順まで。

n8ndockertailscaleclaudemcpxserver-vps

本記事にはプロモーション (アフィリエイト広告) が含まれます。

n8n は、Zapier や Make と同じ「ノードを線で繋いで自動化を組む」ツールです。違いはソースが公開されていて、自分の VPS に置けば無料・実行回数無制限 で使えることです。

この記事では、n8n を VPS に立てて、3 段階で使えるようにします。

  1. Docker Compose で n8n を起動し、Tailscale 経由でスマホからも HTTPS で開ける状態にする (20 分)
  2. ワークフローの途中に Claude を挟む。例として「毎朝 VPS の状態を Claude に要約させて通知する」を作る
  3. n8n を MCP サーバー として公開し、Claude Code から n8n のワークフローを道具として呼べるようにする

3 まで行くと、Claude Code に「VPS のディスク空き容量を見て」と頼むだけで、n8n のワークフローが走って結果が返ってきます。

n8n Cloud とセルフホストの違い

n8n Cloud セルフホスト
料金 月 20 ユーロ〜 (Starter、年払い) VPS 代のみ
実行回数 Starter 2,500 回 / Pro (50 ユーロ) 10,000 回 無制限
更新 自動 自分で docker compose pull
外部からの Webhook そのまま届く 公開設定が必要 (後述)
データの置き場所 n8n 社のサーバー 自分の VPS

セルフホストが向いているのは、実行回数が多い、扱うデータを外に出したくない、同じ VPS で動いているもの (Ollama やデータベース) と直接繋ぎたい、のどれかに当てはまる場合です。逆に、GitHub や Stripe からの Webhook を受ける用途が中心なら、Cloud の方が楽です。

この記事で使う環境

XServer VPS クラウド
4GB / NVMe 50GB で月 2,480 円 (12 か月契約なら 2,068 円)。n8n と Claude Code の同居なら 4GB で足ります。Ollama も載せるなら 8GB を。
公式サイトを見る

1. Docker Compose で立てる

mkdir -p ~/n8n && cd ~/n8n

compose.yaml を置きます。

# ~/n8n/compose.yaml
services:
  n8n:
    image: docker.n8n.io/n8nio/n8n
    container_name: n8n
    ports:
      - "127.0.0.1:5678:5678"   # ループバックだけに束縛。外には出さない
    environment:
      N8N_HOST: "vpsnotes-001.tailc121dc.ts.net"     # Tailscale の MagicDNS 名 (後述)
      N8N_PROTOCOL: "https"
      N8N_PORT: "5678"
      WEBHOOK_URL: "https://vpsnotes-001.tailc121dc.ts.net/"
      GENERIC_TIMEZONE: "Asia/Tokyo"
      TZ: "Asia/Tokyo"
      N8N_RUNNERS_ENABLED: "true"
    volumes:
      - n8n_data:/home/node/.n8n
    restart: unless-stopped

volumes:
  n8n_data:

ポイントは 2 つです。

tailc121dc の部分は自分の tailnet 名です。Mac から ping vpsnotes-001 を打ったときに vpsnotes-001.tailc121dc.ts.net のようにフルネームで表示されるので、そこで分かります。Tailscale の管理画面の DNS タブにも出ています。

docker compose up -d
docker compose logs -f
n8n  | n8n ready on ::, port 5678
n8n  | Version: 2.39.8
n8n  | Editor is now accessible via:
n8n  | https://vpsnotes-001.tailc121dc.ts.net

この記事の時点のバージョンは 2.39.8 です。ログに Error fetching from Strapi API (https://api.n8n.io/api/mcp-servers): timeout のようなエラーが出ることがありますが、これは n8n が MCP サーバーのカタログを外部から取りに行って失敗しただけで、動作には影響しません。

起動直後のメモリはこうでした。

docker stats --no-stream n8n
NAME      MEM USAGE / LIMIT     CPU %
n8n       931.1MiB / 3.818GiB   0.05%

n8n だけで 930MB 使います。Claude Code の常駐と合わせると、ホスト全体で 1.8GB、空きが 2.0GB です。4GB プランで n8n + Claude Code は問題ありませんが、ここに Ollama も載せるなら 8GB が要ります。

2. Tailscale Serve で HTTPS にする

ループバックに束縛した 5678 番を、Tailscale の serve で tailnet の中だけに HTTPS 付きで公開します。証明書は Tailscale が Let’s Encrypt から自動で取ります。

先に、Tailscale の管理画面 (https://login.tailscale.com/admin/dns) の下の方にある HTTPS Certificates を有効にしておきます。これがないと証明書が取れません。

Tailscale の管理画面。HTTPS Certificates を Enable にする

VPS で serve を有効にします。

sudo tailscale serve --bg 5678
sudo tailscale serve status
Available within your tailnet:

https://vpsnotes-001.tailc121dc.ts.net/
|-- proxy http://127.0.0.1:5678

Serve started and running in the background.
https://vpsnotes-001.tailc121dc.ts.net (tailnet only)
|-- / proxy http://127.0.0.1:5678

Mac やスマホのブラウザで https://vpsnotes-001.tailc121dc.ts.net を開くと n8n の画面が出ます。証明書は Let’s Encrypt から自動で取られ、私の環境では serve を打ってから 1 分以内にブラウザで開けました。ufw で 5678 を開ける必要はなく、Tailscale に繋いでいないデバイスからは届きません。実際に VPS のグローバル IP に対して 5678 番と 443 番を叩いても、どちらもタイムアウトします。

最初に開いたときに、オーナーアカウント (メールアドレスとパスワード) の作成を求められます。これは n8n の中だけで使う認証で、外部には送られません。

そのあと案内が 3 つ続きますが、全部飛ばして構いません。

  1. アンケート (会社の種類、役割、用途など)。統計用で、動作に影響しません
  2. 無料ライセンスキーの案内。メールを登録すると、デバッグ機能とフォルダ機能が開放されます。実質はメルマガ登録で、後から設定画面で申請できるので「Skip」で
  3. AI アシスタントの案内。n8n 社のクラウドに繋ぐ機能で、セルフホストの目的からすると不要です。「Set up later in Settings」で

アンケート画面

無料ライセンスの案内。Skip でよい

AI アシスタントの案内。後回しでよい

「Let’s build your first automation」の画面が出れば、セットアップは完了です。

セットアップ完了後の最初の画面

外部からの Webhook を受けたいとき

serve は tailnet の中だけです。GitHub や Slack のような外部サービスから Webhook を受けるには、そのパスだけをインターネットに出す必要があります。tailscale funnel を使うと、指定したパスだけを公開できます。

sudo tailscale funnel --bg --set-path /webhook http://127.0.0.1:5678/webhook

エディタ画面までインターネットに出すのは避けてください。Webhook のパスだけで十分です。この記事では外部からの Webhook を使わないので、funnel は試していません。使う前に tailscale funnel status で、/ (エディタ) が公開に含まれていないことを確認してください。

3. Claude をワークフローに挟む

n8n には Anthropic ノードがあり、API キーを設定すると Claude をワークフローの途中に置けます。もっと複雑な「AI Agent」ノード (ツールを持たせて自律的に動かす) もありますが、この記事は「渡した文章を要約させる」だけなので、Anthropic ノードを直接使います。

API キーを発行する

Anthropic Console (https://console.anthropic.com/) の「API キー」→「キーを作成」で発行します。claude.ai のサブスクリプションとは別の従量課金で、初回は支払い方法の登録が要ります。この記事の用途 (1 日数回の短い要約) なら月に数十円です。

Anthropic Console の API キー画面

「ID 連携を使えば API キーは不要です」という案内が出ますが、これは GCP や GitHub Actions のようなクラウド側で使う仕組みで、自分の VPS の n8n には使えません。「API キーで続ける」を選びます。

ID 連携の案内。API キーで続ける

名前は n8n-vpsnotes のように用途が分かるものにします。有効期限に注意 してください。初期値が 30 日になっていることがあり、そのままだと 1 か月後にワークフローが止まります。常駐で使うなら無期限か、長いものを選びます。

キーの作成。有効期限を確認する

作成後に表示されるキーは一度しか見られないので、そのまま n8n に貼ります。

n8n に登録する

n8n の左上「+」→「New credential」→ 検索欄に Anthropic → Continue。

左上の + から New credential

Anthropic を選ぶ

API Key の欄にキーを貼って Save します。他の欄 (Base URL、Custom Header、Allowed Domains) は初期値のままで構いません。

API Key を貼って Save

Save すると n8n がキーを使って Anthropic の API に接続し、「Connection tested successfully」と出ます。

接続テスト成功

まず Claude が呼べることを確かめる

いきなり全部組む前に、Claude に 1 行返させるだけの最小のワークフローで経路を確かめます。

  1. 左上「+」→「New workflow」
  2. キャンバスの「+」→ 検索 ManualTrigger manually
  3. その右の「+」→ 検索 AnthropicAnthropic ノード。Operation は「Message a Model」
  4. Credential に Anthropic account、Model に claude-sonnet-5、Prompt に VPS の運用担当として、今日の一言を 30 字で。
  5. 「Execute step」

初回はここでエラーになりました。

Bad request のエラー。実体はクレジット残高

「Bad request - please check your parameters」と出るので設定ミスに見えますが、詳細を読むと Your credit balance is too low です。Anthropic の API は 前払い制 で、キーを作っただけでは残高が 0 です。Console の Plans & Billing で最小額 (5 ドル) を買うと通ります。この記事の用途なら数か月持ちます。

成功。Claude の応答が右側に出る

「落ちない設計より、落ちても気づける設計を。」と返ってきました。経路は通っています。

例: 毎朝 VPS の状態を Claude に要約させる

やることは単純です。VPS で free -hdf -h を実行し、その出力を Claude に渡して「気になる点があれば指摘して」と頼み、結果を通知します。

  1. Schedule Trigger: 毎朝 8 時
  2. SSH: free -h && df -h / && uptime を実行
  3. Anthropic: 2 の出力を渡して要約させる
  4. Telegram: 3 の結果を送る

n8n のコンテナから VPS 自身に SSH する

2 の SSH ノードが、この記事で一番詰まったところです。n8n は Docker のコンテナの中で動いているので、localhost と書いてもコンテナ自身を指してしまい、VPS には届きません。ホストの Tailscale IP (100.96.34.27) を指定します。

まず VPS 側で、n8n 専用の鍵を作ります。鍵の形式に注意 してください。n8n の SSH ライブラリは新しい OpenSSH 形式 (-----BEGIN OPENSSH PRIVATE KEY-----) の ed25519 鍵を読めず、Cannot parse privateKey: Unsupported key format になります。RSA を PEM 形式で作ります。

ssh-keygen -t rsa -b 4096 -m PEM -f ~/.ssh/n8n_rsa -N "" -C "n8n@vpsnotes-001"
cat ~/.ssh/n8n_rsa.pub >> ~/.ssh/authorized_keys
cat ~/.ssh/n8n_rsa

最後の cat で出る -----BEGIN RSA PRIVATE KEY----- から -----END RSA PRIVATE KEY----- までを、n8n の Credential に貼ります。左上「+」→「New credential」→ SSHSSH Private Key。Host 100.96.34.27、Port 22、Username deploy、Private Key に鍵の全文です。

ここで Save しても、最初は「Couldn’t connect with these settings」になりました。

SSH Credential の接続失敗

原因は ufw です。初期設定Tailscale で、22 番は「tailscale0 からだけ許可」にしました。コンテナからの通信は Docker のブリッジ (192.168.16.x) から来るので、これに弾かれます。コンテナからの SSH だけ許可を足します。

sudo ufw allow from 192.168.16.0/20 to any port 22 proto tcp comment 'n8n container to ssh'
sudo ufw status numbered
     To                         Action      From
     --                         ------      ----
[ 1] Anywhere on tailscale0     ALLOW IN    Anywhere
[ 2] 22/tcp                     ALLOW IN    192.168.16.0/20            # n8n container to ssh
[ 3] Anywhere (v6) on tailscale0 ALLOW IN    Anywhere (v6)

192.168.16.0/20 は Docker が n8n のネットワークに割り当てた範囲です。自分の環境では docker exec n8n ip route で確認できます。インターネット側からの 22 番は閉じたままです。

Credential の「Retry」で通ります。

SSH Credential の接続成功

SSH の出力を Claude に渡す

ワークフローの Trigger と Anthropic の間に SSH ノードを足します。Resource は Command、Operation は Execute、Command に free -h && df -h / && uptime。「Execute step」で stdout に出力が入ります。

SSH ノードの出力。stdout に free / df / uptime の結果

Anthropic ノードの Prompt を、この stdout を差し込む形に書き換えます。Prompt 欄を Expression に切り替えてから、次を入れます。

あなたは VPS の運用担当です。以下は VPS で実行した free / df / uptime の出力です。
メモリ・ディスク・負荷に気になる点があれば指摘し、なければ「問題なし」と 1 行で。日本語で 3 行以内。

{{ $json.stdout }}

{{ $json.stdout }} が前のノードの出力に置き換わります。Fixed のままだと文字列として送られてしまうので、Expression になっていることを確認してください。

Claude の要約。「問題なし」と返ってきた

「メモリ・ディスク・負荷いずれも健全な範囲内です。問題なし。」と返ってきました。これで「SSH で取って Claude に判断させる」の基本形は完成です。

3 ノードが繋がったワークフロー

Telegram に通知する

通知先は Telegram にしました。普段使っていてスマホに通知が来るものなら何でも構いません。Discord や Slack なら Webhook で同じことができます。

Telegram は Bot を作って、その Bot から自分宛てに送る形です。

  1. Telegram で @BotFather を開き、/newbot → 表示名 → ユーザー名 (末尾 bot) の順に答えると、HTTP API token が出る。これは秘密情報なので n8n にだけ貼る
  2. 作った Bot を開いて /start か何か 1 通送る
  3. ブラウザで https://api.telegram.org/bot<トークン>/getUpdates を開くと、"chat":{"id":123456789 の形で Chat ID が出る。反映に数秒かかるので、空なら少し待って再読み込み

n8n で Anthropic ノードの右の「+」→ TelegramSend a text message。Credential を新規作成して Access Token に Bot のトークンを入れると、接続テストが通ります。

Telegram の Credential 登録

Chat ID に 3 で取った数字、Text を Expression で {{ $json.content[0].text }} にして「Execute step」。

Telegram に送信成功

スマホの Telegram に Claude の要約が届きました。末尾に「This message was sent automatically with n8n」という宣伝文が付きますが、Additional Fields の Append n8n Attribution を OFF にすると消せます。

毎朝 8 時に動かす

最後に、手動のトリガーをスケジュールに変えます。右上の「+」→ ScheduleSchedule Trigger。Trigger Interval を Days、Hour を 8、Minute を 0 にして、出力を「Execute a command」に繋ぎます。

Manual Trigger (「When clicking ‘Execute workflow’」) は、クリックすると実行されてしまって選択しにくく、消すのに手間取りました。ドラッグで少し動かしてから離すと選択状態になり、Delete キーで消えます。消さずに残しておいても動作には影響しません。

ワークフロー名を付けて、右上の Publish を押すと有効になります。Publish しないとスケジュールは動きません。

Published になった完成形

この形ができると、「SSH で何か取ってきて Claude に判断させて通知する」パターンは全部同じ作りで組めます。ログの異常検知、バックアップの成否確認、証明書の期限確認などです。

4. n8n を MCP サーバーにして Claude Code から呼ぶ

ここからが、このサイトならではの使い方です。n8n には MCP Server Trigger というノードがあり、ワークフローを MCP の「ツール」として公開できます。Claude Code にそのサーバーを登録すると、Claude Code が必要に応じて n8n のワークフローを呼ぶようになります。

n8n 側: ツールになるワークフローを作る

構造は 2 段です。「MCP Server Trigger を持つワークフロー」が窓口で、その Tools に「実際の処理をするサブワークフロー」をぶら下げます。SSH ノードは Tools に直接は繋げないので (Tools に繋げるのは「〜 Tool」という種類のノードだけ)、SSH を実行するサブワークフローを別に作って、それを「Call n8n Workflow Tool」で呼ぶ形にします。

サブワークフロー disk_and_memory

  1. 「+」→「New workflow」
  2. 最初のノードで「Execute Sub-workflow」カテゴリを開き、Triggers の When executed by Another Workflow を選ぶ

Execute Sub-workflow カテゴリの中に Trigger がある

  1. 右に SSH ノード (Credential は先ほどの SSH Private Key account、Command は free -h && df -h / && uptime)
  2. 名前を disk_and_memory にして Publish

サブワークフロー。トリガー + SSH の 2 ノード

MCP のワークフロー VPS MCP tools

  1. 「+」→「New workflow」→ 最初のノードで MCP を検索 → MCP Server Trigger

MCP Server Trigger の設定画面

  1. Path を vps-tools のような分かりやすい名前に、Instructions に VPS (vpsnotes-001) の状態を確認するツール群。 と書く。Instructions は Claude Code がこのサーバーの用途を把握するのに使われます
  2. 下の Tools の「+」→ workflow で検索 → Call n8n Workflow Tool

Tools の検索。Call n8n Workflow Tool を選ぶ

  1. Workflow に disk_and_memory を選び、Description に VPS (vpsnotes-001) のメモリ・ディスク・稼働時間を取得する。引数は不要。 と書く。この Description が、Claude Code がツールを選ぶときの判断材料になります

Call n8n Workflow Tool の設定

「This node must be connected to an AI agent」という警告が出ますが、MCP Server Trigger の Tools に繋がっていれば動きます。Tools の ◇ と Tool の ◇ が点線で繋がっていることを確認してください。

Tools が繋がった状態

  1. Publish

Authentication は None のままにしました。エンドポイントは Tailscale の中にしか公開していないので、tailnet の外からは届きません。インターネットに出す場合は Bearer にしてトークンを設定してください。

動いているか確かめる

Publish 後のエンドポイントは https://vpsnotes-001.tailc121dc.ts.net/mcp/vps-tools です (Test URL の mcp-testmcp になります)。トランスポートは Streamable HTTP で、curl で initialize を投げると応答します。

curl -s -X POST https://vpsnotes-001.tailc121dc.ts.net/mcp/vps-tools \
  -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
event: message
data: {"result":{"protocolVersion":"2025-03-26","capabilities":{"tools":{}},"serverInfo":{"name":"MCP_Server_Trigger","version":"0.1.0"},"instructions":"VPS (vpsnotes-001) の状態を確認するツール群。"},"jsonrpc":"2.0","id":1}

Claude Code 側: MCP サーバーを登録する

Claude Code を動かしているマシン (この VPS 自身でも、手元の Mac でも) で登録します。Tailscale に繋がっていれば、どちらからでも届きます。

claude mcp add --transport http n8n https://vpsnotes-001.tailc121dc.ts.net/mcp/vps-tools
claude mcp list
n8n: https://vpsnotes-001.tailc121dc.ts.net/mcp/vps-tools (HTTP) - ✔ Connected

あとは普通に話しかけるだけです。

> n8n の MCP ツールを使って、VPS のディスクとメモリの状況を確認して。結果を 3 行で要約して。
VPS (vpsnotes-001) の状況です。

- メモリ: 3.8GiB中1.5GiB使用、available 2.3GiB、swapはほぼ未使用(524Ki/2GiB)で余裕あり
- ディスク: /dev/vda1 48GB中6.8GB使用(15%)、41GB空き
- 稼働: 4日21時間稼働、load average 0.02とほぼアイドル状態、いずれも健全

Claude Code が n8n のツールを呼び、n8n がサブワークフローで SSH を実行し、その結果を Claude Code が読んで答えています。ツール名は「Call n8n Workflow Tool」のノード名から自動で付くので (初期値だと Call_disk_and_memory_)、ノード名を disk_and_memory に変えておくときれいになります。

何が嬉しいのか

Claude Code に「道具」を足す方法はいくつかありますが、n8n を挟むと、道具の中身を画面で組み替えられる のが利点です。SSH で取る情報を増やす、結果を Slack にも流す、といった変更を、Claude Code 側は何も触らずに済ませられます。

逆に、n8n の MCP Client Tool ノードを使えば、n8n のワークフローから外部の MCP サーバーを呼ぶこともできます。こちらは試していないので、別の機会に。

運用

更新

cd ~/n8n
docker compose pull
docker compose up -d

n8n は更新が速いので、月に 1 回くらいは上げておくと、ノードの追加や不具合修正の恩恵を受けられます。

バックアップ

ワークフローと認証情報は n8n_data ボリュームに入っています。

docker run --rm -v n8n_n8n_data:/data -v $(pwd):/backup alpine tar czf /backup/n8n-$(date +%F).tgz -C /data .

docker volume ls で見ると、ボリューム名は n8n_n8n_data (ディレクトリ名 + compose の volume 名) でした。

認証情報は n8n が暗号化して保存しています。暗号化キーは ~/.n8n/config の中にあり、ボリュームごとバックアップすれば一緒に取れます。VPS を作り直すときは、このキーがないと認証情報が復号できません。

費用

初回に 5 ドル分のクレジットを買い、この記事の検証 (十数回の呼び出し) で消費したのは数セントでした。毎朝 1 回の要約なら、5 ドルで半年以上持つ計算です。

つまずいた点

Docker の起動から Telegram 通知まで 1 時間半、MCP で Claude Code から呼べるまで、さらに 1 時間ほどでした。n8n の UI はバージョンで変わるので、ノードの探し方で迷う時間が多かったです。

次にやること

まとめ

XServer VPS クラウド
この記事の検証環境。n8n と Claude Code の同居なら 4GB、Ollama も載せるなら 8GB。
公式サイトを見る