本文へ移動
購読
GUIDE#saas更新 2026.09.24

n8nとは?セルフホストでSaaS連携とAIワークフローを自動化する

n8nの読み方・開発元・ライセンスから、PostgreSQL構成のDocker Compose、HTTPS化、Webhook申請受付とOllama分類のワークフロー、バックアップまでを2.40.5で検証した手順です。

level
中級
time
約45分
stack
Docker Compose / n8n 2.40.5 / PostgreSQL 17 / Ollama(任意)
author
@ryoma_t
// 前提条件
Docker と docker compose が使えるLinuxサーバー / リバースプロキシとTLS証明書の基礎知識
n8nのワークフローエディターで申請受付フローをテスト実行し、社内ドメインの申請が受付完了まで進んだ画面
画像:nagoya.dev(実際の画面・実行結果をもとに作成)

n8n(エヌエイトエヌ)とは、SaaSやAPIの処理を「ノード」としてつなぎ、業務の流れを自動化するワークフローツールです。ドイツ・ベルリンのn8n GmbHが開発し、ソースコードを公開したうえでDockerイメージを配布しているため、自社のサーバーでセルフホストできます。

ここでは情シスが社内向けに運用する前提で、PostgreSQLを使うDocker Compose構成、HTTPS化で必要な環境変数、Webhookで申請を受け付けるワークフロー、Ollamaで問い合わせを分類するAIワークフロー、バックアップと更新までを、n8n 2.40.5(執筆時点の安定版)で実際に動かした結果をもとに手順にしています。1.x時代の記事と環境変数やCLIが変わっている箇所は、その都度明記します。

この記事でわかること

  • n8nの読み方・開発元・ライセンスと、無償のコミュニティ版では使えない機能
  • Power AutomateやSaaS型ツールと比べて、セルフホストのn8nを選ぶ条件
  • PostgreSQL構成のcompose.yamlと、HTTPS化で必要な N8N_WEBHOOK_URL・N8N_PROXY_HOPS
  • Webhook→IF→応答のワークフローと、テストURL/本番URL・公開(Publish)の関係
  • Ollamaで問い合わせを分類する構成、暗号化キーを含むバックアップ、実際に出たエラーと対処

n8nとは:読み方・開発元・ライセンスの前提

名前は「nodemation(node+automation)」の先頭と末尾のnの間に8文字あることから来た略記で、読みはエヌエイトエヌです。画面上でトリガー(Webhook、スケジュール、フォームなど)とアクション(HTTPリクエスト、Slack投稿、DB書き込みなど)のノードを線でつなぎ、1つの流れを「ワークフロー」として保存します。分岐・ループ・エラー時の別ルートを画面で組めるうえ、JavaScriptを書けるCodeノードもあるため、ノーコードで始めてコードで詰める使い方ができます。

ライセンスはOSI準拠のオープンソースではなく、n8n社が「fair-code」と呼ぶSustainable Use Licenseです。自社の業務のためにセルフホストして使う分には無償ですが、n8nそのものを顧客向けの有償サービスとして提供したり、販売する製品に組み込んだりする場合は条件が変わります。社外向けの受託開発や製品に絡める予定があるなら、リポジトリ内のライセンス文書と公式の説明を先に確認してください。

無償のコミュニティ版で何が使えないかは、起動したインスタンスの設定情報(/rest/settings)で確認できます。2.40.5では、ワークフローや認証情報をユーザー間で共有する機能、SAML・OIDC・LDAPによるSSO、Git連携(ソース管理)、外部シークレット、ログストリーミング、MFAの強制がすべて無効(ライセンスが必要)でした。一方でユーザー数に上限はなく、各ユーザーが自分でTOTPのMFAを設定することはできます。「部署ごとにワークフローを分けて共有したい」「Entra IDでログインさせたい」という要件がある場合は、最初から有償ライセンスかクラウド版を前提に見積もるのが現実的です。

Power AutomateやSaaS型ツールとの違い

自動化ツールは「どこで実行されるか」で向き不向きがほぼ決まります。セルフホストのn8nは社内ネットワークの中で動くため、社内APIやDBに直接届く代わりに、更新・バックアップ・監視を自分で持つことになります。

観点n8n(セルフホスト)Power AutomateSaaS型(Zapier・Makeなど)
実行場所自社のサーバーMicrosoftのクラウドベンダーのクラウド
社内システムへの到達同じネットワークなら直接オンプレミスデータゲートウェイ経由原則インターネットに公開されたAPIのみ
課金の考え方(執筆時点)コミュニティ版は無償、組織向け機能は有償ライセンスMicrosoft 365付属の範囲+プレミアム機能は追加ライセンスタスク数・オペレーション数などの従量
Microsoft 365との統合ノードはあるがアプリ登録とOAuth設定が必要承認フロー・Teams・Entra IDと最も深く統合コネクタ経由
運用の負担自分で持つベンダー側ベンダー側

判断の目安は次のとおりです。社内のファイルサーバーや基幹DB、閉域のAPIに触る処理が中心で、実行回数に応じた課金を避けたいならn8nのセルフホストが合います。Microsoft 365の中で完結する承認や通知が中心ならPower Automateのほうが設定も権限管理も少なく済みます。サーバーの面倒を見る人がいない、あるいはベンダーのSLAが必要な場合は、n8nを選ぶとしてもクラウド版にするべきです。

構成を決める:データベースと実行モード

n8nは何も指定しないとSQLiteを使い、/home/node/.n8n の中にデータベースファイルを作ります。1人で試すならそれで十分ですが、社内で複数人が使い、実行履歴がたまっていく運用ではPostgreSQLを選びます。バックアップを pg_dump で取れること、ファイル破損のリスクが小さいこと、あとからワーカーを足すキューモードに移れることが理由です。

実行モードは既定の regular(1プロセスでエディターも実行も担当)から始めれば足ります。同時に届くWebhookが多い、重い処理が画面操作を遅くする、といった症状が出てから、Redisとワーカー用のコンテナを追加するキューモードを検討してください。参考までに、筆者の検証環境ではワークフローを数本動かした状態でn8nのコンテナが約700MB、PostgreSQLが約65MBのメモリを使っていました。仮想マシンなら2GB以上を割り当てておくと余裕があります。

Docker Composeでn8nを起動する

作業用ディレクトリで、データベースのパスワードと暗号化キーを .env に生成します。N8N_ENCRYPTION_KEY は、n8nに保存するSaaSのAPIキーやOAuthトークンを暗号化する鍵です。

terminal
mkdir -p ~/n8n && cd ~/n8n
printf 'POSTGRES_PASSWORD=%s\nN8N_ENCRYPTION_KEY=%s\n' "$(openssl rand -hex 24)" "$(openssl rand -hex 32)" > .env
chmod 600 .env

compose.yamlは次のとおりです。ポートは 127.0.0.1 にだけ公開し、外部からはあとで設定するリバースプロキシ経由でアクセスさせます。イメージのタグは latest にせず、検証したバージョンに固定します。

compose.yaml
services:
  postgres:
    image: postgres:17-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: n8n
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]
      interval: 5s
      retries: 10

  n8n:
    image: docker.n8n.io/n8nio/n8n:2.40.5
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      DB_TYPE: postgresdb
      DB_POSTGRESDB_HOST: postgres
      DB_POSTGRESDB_DATABASE: n8n
      DB_POSTGRESDB_USER: n8n
      DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD}
      N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
      GENERIC_TIMEZONE: Asia/Tokyo
      TZ: Asia/Tokyo
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  pgdata:
  n8n_data:

GENERIC_TIMEZONE はスケジュールトリガーや日付関数が使うタイムゾーンで、未設定だとUTCで動きます。毎朝9時の定期実行が18時に走る、という事故を防ぐために必ず入れます。起動したら、データベースのマイグレーションが終わるまで待ちます。

terminal
docker compose up -d
curl -s http://127.0.0.1:5678/healthz
curl -s http://127.0.0.1:5678/healthz/readiness

/healthz はプロセスが生きていれば {"status":"ok"} を返し、/healthz/readiness はDB接続とマイグレーションが終わるまで {"status":"error"} を返します。検証では起動直後の十数秒はreadinessだけがerrorでした。監視やロードバランサーのヘルスチェックには readiness のほうを使います。

N8N_ENCRYPTION_KEY を指定しないと、n8nは初回起動時に鍵を自動生成して /home/node/.n8n/config に書き込みます。この場合、ボリュームを消すかコンテナを作り直した時点で、保存済みの認証情報は「Credentials could not be decrypted. The likely reason is that a different "encryptionKey" was used to encrypt the data.」となって復号できなくなります。環境変数で明示し、同じ値をパスワードマネージャーなどDBとは別の場所に保管してください。configファイルと環境変数の値が食い違うと「Mismatching encryption keys」で起動自体が止まります。

HTTPSで公開する:リバースプロキシと環境変数

ブラウザーで http://127.0.0.1:5678 を開くと、最初にオーナーアカウントの作成画面が出ます。ここで作ったユーザーがインスタンスの管理者になるため、個人ではなく情シスの管理用メールアドレスで作っておくと、担当者が異動したときに困りません。メール送信(SMTP)を設定していない間は、ユーザーを招待すると招待リンクが画面に表示されるので、それを本人に渡します。

社内の他のPCから使わせるときは、nginxやCaddyなどのリバースプロキシでHTTPSを終端し、n8n側には外から見えるURLを教えます。n8n 2.xでは、1.xの記事でよく見る WEBHOOK_URL は非推奨になり、起動時に「Use N8N_WEBHOOK_URL instead」という警告が出ます。

compose.yaml(n8nサービスのenvironmentに追加)
      N8N_HOST: n8n.example.co.jp
      N8N_PROTOCOL: https
      N8N_WEBHOOK_URL: https://n8n.example.co.jp/
      N8N_PROXY_HOPS: "1"
      N8N_DIAGNOSTICS_ENABLED: "false"

N8N_WEBHOOK_URL を入れると、Webhookノードに表示されるURLと、OAuth2の認証情報画面に表示されるリダイレクトURL(https://n8n.example.co.jp/rest/oauth2-credential/callback)の両方が外向きのURLになります。検証でもこの5行を足しただけで、設定画面が返すコールバックURLが http://localhost:5678/... からhttpsの外部URLに変わりました。N8N_PROXY_HOPS は手前にいるプロキシの段数で、これがないとプロキシが付けた X-Forwarded-For を信用せず、ログにエラーが出ます。N8N_DIAGNOSTICS_ENABLED は利用状況の送信で、既定は有効です。

リバースプロキシ側は、Hostヘッダーを引き継ぐこととWebSocketを通すことの2点が要です。n8nのエディターは /rest/push にWebSocketでつなぎ、その際にOriginヘッダーとHost(または X-Forwarded-Host)が一致するかを検証します。nginxは何も書かないとHostをプロキシ先のアドレスに書き換えるため、proxy_set_header Host $host; がないとエディターに「Connection lost」が出続けます。

/etc/nginx/conf.d/n8n.conf(抜粋)
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 443 ssl;
    server_name n8n.example.co.jp;
    # ssl_certificate / ssl_certificate_key は環境に合わせて設定

    location / {
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 300s;
    }
}

プロキシ全体の書き方や証明書の扱いはnginxでリバースプロキシを構築するガイドにまとめています。n8nのWebhookを社外のSaaSから受ける必要がなければ、公開範囲は社内ネットワークかVPNの中にとどめておくのが安全です。

最初のワークフロー:Webhookでアカウント申請を受け付ける

最初の題材は、SaaSアカウントの発行依頼をJSONで受け付け、社内ドメインのメールアドレスならチケット番号を返し、社外ドメインなら400を返すワークフローです。ノードは5つで、分岐と応答の書き方という、n8nの使い方の基本がひととおり入っています。

  1. 申請を受信(Webhook):POSTを account-request で受ける。応答は「Respond to Webhook」ノードに任せる設定にする
  2. 社内ドメインか(IF):{{ $json.body.email }} が @example.co.jp で終わるかを判定する
  3. チケット作成(Edit Fields):REQ-{{ $execution.id }} のような受付番号と受付日時を組み立てる
  4. 受付完了を返す(Respond to Webhook):チケット作成の結果をそのまま201で返す
  5. エラーを返す(Respond to Webhook):IFがfalseのとき、エラーメッセージを400で返す

画面で組んでもよいのですが、次のJSONをコピーしてエディターのキャンバス上で貼り付ける(Ctrl+V)と、ノードと接続がそのまま再現されます。

account-request.json
{
  "id": "acctRequest00001",
  "name": "SaaSアカウント申請の受付",
  "nodes": [
    {
      "parameters": { "httpMethod": "POST", "path": "account-request", "responseMode": "responseNode", "options": {} },
      "name": "申請を受信", "type": "n8n-nodes-base.webhook", "typeVersion": 2.1,
      "position": [0, 0], "webhookId": "5d0f2a4e-7a51-4c2b-9d3e-1f6a8b2c4e01"
    },
    {
      "parameters": {
        "conditions": {
          "options": { "caseSensitive": false, "leftValue": "", "typeValidation": "strict", "version": 2 },
          "conditions": [
            { "id": "c1", "leftValue": "={{ $json.body.email }}", "rightValue": "@example.co.jp",
              "operator": { "type": "string", "operation": "endsWith" } }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "name": "社内ドメインか", "type": "n8n-nodes-base.if", "typeVersion": 2.2, "position": [220, 0]
    },
    {
      "parameters": {
        "assignments": { "assignments": [
          { "id": "a1", "name": "ticketId", "value": "=REQ-{{ $execution.id }}", "type": "string" },
          { "id": "a2", "name": "requester", "value": "={{ $json.body.name }}", "type": "string" },
          { "id": "a3", "name": "apps", "value": "={{ $json.body.apps.join(', ') }}", "type": "string" },
          { "id": "a4", "name": "receivedAt", "value": "={{ $now.toFormat('yyyy-MM-dd HH:mm') }}", "type": "string" }
        ] },
        "options": {}
      },
      "name": "チケット作成", "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [440, -100]
    },
    {
      "parameters": { "respondWith": "firstIncomingItem", "options": { "responseCode": 201 } },
      "name": "受付完了を返す", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.1, "position": [660, -100]
    },
    {
      "parameters": { "respondWith": "json", "responseBody": "{\"error\": \"社外ドメインのアドレスは受け付けません\"}", "options": { "responseCode": 400 } },
      "name": "エラーを返す", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.1, "position": [440, 100]
    }
  ],
  "connections": {
    "申請を受信": { "main": [[ { "node": "社内ドメインか", "type": "main", "index": 0 } ]] },
    "社内ドメインか": { "main": [
      [ { "node": "チケット作成", "type": "main", "index": 0 } ],
      [ { "node": "エラーを返す", "type": "main", "index": 0 } ]
    ] },
    "チケット作成": { "main": [[ { "node": "受付完了を返す", "type": "main", "index": 0 } ]] }
  },
  "settings": { "executionOrder": "v1" }
}

URLはWebhookノードに「Test URL」と「Production URL」の2種類が表示されます。テストURL(/webhook-test/account-request)は、エディターで「Execute workflow」を押して待ち受けている間の1回だけ応答し、各ノードの入出力が画面に残るので、組み立て中の確認に使います。本番URL(/webhook/account-request)は、ワークフローを公開(Publish)すると常時応答します。n8n 2.xでは編集中の下書きと公開中のバージョンが分かれていて、公開ボタンを押すまで編集内容は本番URLの動作に反映されません。

画面を使わずに登録する場合はCLIでも取り込めます。2.xでは有効化のコマンドが update:workflow --active=true から publish:workflow に変わりました。

terminal
docker compose cp account-request.json n8n:/tmp/account-request.json
docker compose exec n8n n8n import:workflow --input=/tmp/account-request.json
docker compose exec n8n n8n publish:workflow --id=acctRequest00001
docker compose exec n8n n8n list:workflow

publish:workflow は「n8nが起動中なら再起動するまで反映されない」という注意を表示します。検証環境(PostgreSQL構成)ではその場で本番URLが応答しましたが、404が返るようなら docker compose restart n8n を実行してください。

CLIの import:workflow は、JSONに id がないと「null value in column "id" of relation "workflow_entity" violates not-null constraint」で失敗します。エディターからの貼り付けやファイル取り込みはIDを自動で振りますが、CLIでは英数字16文字程度の id を自分で入れておく必要があります。同じIDで再度取り込むと上書きになる点も、バックアップからの戻しで使えます。

SaaSとつなぐ:認証情報とHTTP Requestノード

SaaSにつなぐノードは、APIキーやOAuthトークンを「Credentials(認証情報)」として別に保存し、ノードからはそれを参照します。OAuth2を使うSaaS(Microsoft 365、Google、Slackなど)は、SaaS側のアプリ登録画面にn8nのリダイレクトURLを登録する必要があり、ここに前述の https://n8n.example.co.jp/rest/oauth2-credential/callback を入れます。N8N_WEBHOOK_URL を設定しないまま認証情報の画面を開くと、表示されるのは http://localhost:5678 で始まるURLです。これをSaaS側に登録すると、社内の他のPCからは認可後の戻り先に到達できず、あとでn8n側を直した時点で今度はSaaS側が「redirect_uriが一致しない」エラーを返します。

2.40.5にはMicrosoft Entra ID(ユーザー・グループ操作)、Microsoft Teams、Slack、Google Sheets、Mattermost、Nextcloudなどのノードがありますが、kintoneとChatworkの専用ノードはありません。この場合はHTTP RequestノードでREST APIを直接呼びます。kintoneなら、認証情報の種類に「Header Auth」を選んでヘッダー名 X-Cybozu-API-Token、値にAPIトークンを入れ、HTTP Requestノードで https://{サブドメイン}.cybozu.com/k/v1/records.json?app=12(アプリ番号12は例)のようなURLを叩きます。ヘッダーをノードに直接書くと、ワークフローをエクスポートしたJSONにトークンが平文で残るため、必ず認証情報側に置きます。kintone側の権限設計はkintoneとは?情シスが導入前に確認すべき権限設計で整理しています。

認証情報は「用途ごとに1つ、最小権限で」作るのが原則です。たとえばEntra IDのユーザー作成に使うアプリ登録には、ユーザーの読み書きに必要な権限だけを与え、ワークフローの作成者個人ではなく情シスの管理用アカウントで承認しておきます。コミュニティ版では認証情報をほかのユーザーと共有できないため、運用担当のアカウントを1つ決め、本番用のワークフローはそのアカウントで作る運用にしておくと、担当者の退職時にワークフローが止まる事態を避けられます。

AIワークフロー:Ollamaで問い合わせを分類する

n8nにはLLMを呼ぶノード群があり、社内のOllamaを使えば問い合わせ本文を外部に出さずに分類や要約ができます。構成は「Basic LLM Chain」ノードに「Ollama Chat Model」ノードをサブノードとしてつなぐ形で、プロンプトはBasic LLM Chain側に書きます。

  • Ollamaを同じcompose.yamlに ollama というサービス名で追加し、認証情報「Ollama」のBase URLに http://ollama:11434 を入れる
  • Basic LLM Chainのプロンプト例:「次の問い合わせを『アカウント』『端末』『ネットワーク』『その他』のどれか1つに分類し、カテゴリ名だけを出力してください。問い合わせ: {{ $json.text }}」
  • Ollama Chat Modelのオプションで Temperature を0、Num Predict(最大出力トークン数)を16程度にして、長い回答を生成させない

Base URLに http://localhost:11434 と書くと、認証情報の接続テストは「ECONNREFUSED」になります。n8nのコンテナから見たlocalhostはn8nのコンテナ自身だからです。サービス名で書き直すと「Connection successful!」に変わります。モデルの導入と選び方はOllamaで始めるローカルLLM入門を参照してください。

モデルの大きさは結果に直結します。検証で0.5Bパラメータの小型モデル(qwen2.5:0.5b)を使ったところ、ワークフロー自体は成功したものの、出力は「問い合わせの内容は、アカウント、端末、ネットワーク、」というカテゴリ名にならない文でした。しかも4コアのCPUのみ・他の処理と相乗りの環境では、生成速度が1秒あたり1トークンを下回りました。LLMの出力をそのまま後続の分岐に使わず、SwitchノードやIFノードで「4つのカテゴリ名のどれかと完全一致するか」を検証し、外れたら「その他」に倒して人が見る、という受け皿を必ず作ってください。

運用:バックアップ・実行履歴・更新

バックアップは3点セットで考えます。PostgreSQLのダンプ(ワークフロー・認証情報・実行履歴のすべて)、CLIでのエクスポート(人が読めるJSONで差分を追える)、そして暗号化キーです。キーがなければ、ダンプから戻しても認証情報は復号できません。

terminal
docker compose exec postgres pg_dump -U n8n -d n8n -Fc -f /tmp/n8n.dump
docker compose cp postgres:/tmp/n8n.dump ./backup/n8n-$(date +%F).dump
docker compose exec -u node n8n n8n export:workflow --backup --output=/home/node/.n8n/backup/workflows/
docker compose exec -u node n8n n8n export:credentials --backup --output=/home/node/.n8n/backup/credentials/

export:credentials は既定で暗号化されたまま書き出します(ファイルの data が U2FsdGVkX1 で始まる文字列になります)。--decrypted を付けると全APIキーが平文になるため、別の暗号化キーを持つ環境へ移行する作業のときだけ使い、終わったらファイルを消します。

実行履歴(Executions)は、2.40.5の既定で成功・失敗とも保存され、336時間(14日)または10,000件を超えたものから削除されます。履歴にはWebhookで受けた氏名やメールアドレスもそのまま残るため、社内の保存期間の規程に合わせて EXECUTIONS_DATA_MAX_AGE(時間単位)を短くするか、成功時は保存しない設定(EXECUTIONS_DATA_SAVE_ON_SUCCESS=none)にします。

更新はイメージのタグを1段ずつ上げ、上げる前にPostgreSQLのダンプを取ります。n8nはマイナーバージョンの更新が速く、起動ログに「There are deprecations related to your n8n setup」として、次のバージョンで既定値が変わる環境変数が列挙されます。更新のたびにこの一覧を読み、n8n audit で使われていない認証情報や危険なノードの利用がないかを確認するところまでを定例作業にしておくと、メジャーバージョンの破壊的変更に慌てずに済みます。

セキュリティの既定値と追加で締める設定

2.xでは、1.xで問題になりやすかった機能がいくつか既定で閉じられています。2.40.5のイメージに含まれる設定値を確認した結果と、追加で推奨する設定をまとめます。

項目既定値(2.40.5で確認)推奨
Execute Command / Local File Trigger ノード無効(NODES_EXCLUDE に登録済み)有効化しない。サーバー上のコマンド実行はワークフロー作成者全員にシェルを渡すのと同じ
Codeノードからの process.env 参照ブロックそのまま。秘密情報は認証情報に置く
ファイル読み書きの範囲~/.n8n-files に制限そのまま
SSRF保護(内部アドレスへのリクエスト制限)無効多人数で使うなら N8N_SSRF_PROTECTION_ENABLED=true にし、呼んでよい社内ホストを許可リストに入れる
利用状況の送信有効N8N_DIAGNOSTICS_ENABLED=false
セキュアCookie有効HTTPSで運用し、無効化しない
コミュニティノードのインストール有効使わないなら N8N_COMMUNITY_PACKAGES_ENABLED=false

特にSSRF保護は見落とされがちです。HTTP Requestノードは社内の任意のアドレスにリクエストを送れるため、ワークフローを作れる人は「n8nのサーバーから届く範囲」をすべて叩けます。作成者を情シスに限るか、SSRF保護で宛先を絞るかのどちらかを選んでください。Codeノードでpythonを使いたい場合、n8n本体のイメージにはPythonが入っておらず、起動ログに「Failed to start Python task runner in internal mode. because Python 3 is missing from this system.」と出ます。Pythonは n8nio/runners イメージを別コンテナで動かす外部モードで使う設計になっています。

動作確認

構築後に確認するコマンドと、検証環境で得られた出力です。申請の受け付けは、社内ドメインと社外ドメインの2通りを送って両方の分岐を通します。

terminal
curl -s http://127.0.0.1:5678/healthz/readiness
# {"status":"ok"}

curl -s -X POST http://127.0.0.1:5678/webhook/account-request \
  -H 'Content-Type: application/json' \
  -d '{"name":"山田 太郎","email":"[email protected]","apps":["kintone","Slack"]}'
# {"ticketId":"REQ-2","requester":"山田 太郎","apps":"kintone, Slack","receivedAt":"2026-09-24 07:53"}

curl -s -o /dev/null -w '%{http_code}\n' -X POST http://127.0.0.1:5678/webhook/account-request \
  -H 'Content-Type: application/json' -d '{"name":"外部 花子","email":"[email protected]","apps":["Slack"]}'
# 400

receivedAt が日本時間になっていれば GENERIC_TIMEZONE が効いています。エディターの「Executions」タブに2件の実行が残り、1件目が上のルート、2件目が下のルートを通っていれば分岐も正しく動いています。CLIでワークフローを1回だけ実行したい場合は n8n execute --id=<ID> を使いますが、起動中のコンテナ内で実行すると「n8n Task Broker's port 5679 is already in use」で止まるため、docker compose exec -e N8N_RUNNERS_BROKER_PORT=5680 n8n n8n execute --id=<ID> のように別ポートを指定します。

つまずきポイント

「Your n8n server is configured to use a secure cookie, however you are either visiting this via an insecure URL, or using Safari.」と表示される

http://192.168.x.x:5678 のように、localhost以外のアドレスにHTTPでアクセスすると出ます。n8nはログイン用のCookieにSecure属性を付けるため、HTTPSでないとブラウザーがCookieを保存しません。正しい対処はリバースプロキシでHTTPSにすることです。N8N_SECURE_COOKIE=false でも表示は消えますが、社内LANであってもセッションCookieが平文で流れるため、検証用の使い捨て環境以外では使わないでください。

Webhookが404で「The requested webhook "account-request" is not registered.」を返す

テストURL(/webhook-test/)を叩いた場合は、エディターで「Execute workflow」を押して待ち受け状態にしていないのが原因で、応答の hint にも同じ説明が出ます。本番URL(/webhook/)で出る場合は、ワークフローが公開されていないか、公開後に編集した内容をまだ公開していないかのどちらかです。CLIで公開した直後なら、n8nを再起動すると登録されます。

ログに「ValidationError: The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false」が出る

リバースプロキシの後ろで N8N_PROXY_HOPS を設定していないときに出ます(エラーコードは ERR_ERL_UNEXPECTED_X_FORWARDED_FOR)。この状態ではログイン試行の回数制限がプロキシのIPアドレス単位で数えられるため、誰か1人がパスワードを間違え続けると、同じプロキシを通る全員が一時的にログインできなくなるおそれがあります。プロキシが1段なら N8N_PROXY_HOPS=1 を入れて再起動すると消えます。

Ollamaの認証情報テストが「ECONNREFUSED」になる

Base URLを http://localhost:11434 にしているのが原因です。同じcompose.yamlで動かしているならサービス名(http://ollama:11434)に、ホストOSで動かしているなら、Linuxではcompose.yamlの extra_hosts に host.docker.internal:host-gateway を足したうえで http://host.docker.internal:11434 にします。後者の場合、Ollamaがホスト側で 127.0.0.1 にしか待ち受けていないと届かないため、待ち受けアドレスも確認してください。

よくある質問

n8nは無料で使えますか?

セルフホストのコミュニティ版は、自社の業務で使う限り無償です。ただしユーザー間でのワークフロー共有、SSO、Git連携、外部シークレットなどは有償ライセンスが必要で、サーバー代と運用の手間は自社持ちになります。クラウド版は実行回数などで上限が決まる有償プランです(執筆時点、料金は公式サイトで確認してください)。

n8nとは何の略ですか?読み方は?

「nodemation」の略記で、先頭のnと末尾のnの間に8文字あることからn8nと書きます。読みはエヌエイトエヌです。ノード(node)をつないで自動化(automation)するという製品の考え方がそのまま名前になっています。

n8nはどこの会社の製品ですか?

ドイツ・ベルリンに本社を置くn8n GmbHが開発しています。ソースコードはGitHubで公開されていますが、ライセンスはOSI準拠のオープンソースではなく、同社が定めたSustainable Use License(fair-code)です。社内利用は無償で、顧客向けに有償で提供する場合は条件が異なります。

n8nは何に使うのですか?

情シスの用途では、アカウント申請フォームの受付と通知、SaaS間のデータ同期、定期的なレポート作成、監視アラートのチャットへの転送、問い合わせのAI分類などが代表的です。社内ネットワークのAPIやDBに直接届くため、クラウド型の自動化ツールでは扱いにくい社内システムとの連携に向いています。

まとめ

n8nのセルフホストは、PostgreSQL構成で起動し、HTTPS化の際に N8N_WEBHOOK_URL と N8N_PROXY_HOPS を入れ、nginxではHostヘッダーとWebSocketを通す、という3点を押さえれば社内運用に乗せられます。ワークフローはWebhook→IF→応答の形から始め、テストURLで確かめてから公開します。AIノードは出力を検証するノードとセットで使い、暗号化キーはDBのダンプとは別に保管してください。

次に読む

$ subscribe --weekly
毎週月曜、企業ITとホームラボの要点を5分で。