SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor

VPSにn8nをDockerとHTTPSで構築する方法

VPS上でn8nをDocker Compose、Postgres、リバースプロキシで構築します。WEBHOOK_URLと暗号化キーの落とし穴、よくあるエラー文字列をまとめて解説します。

構築するもの

n8n はワークフロー自動化ツールです。トリガー、Webhook、スケジュール、フォーム送信を起点に、API の呼び出し、データの変換、他のシステムへの書き込みを行うノードの連鎖を、ビジュアルエディターで構築できます。サービスを自分で実装しなくても、あらゆるモデルプロバイダーやデータベースと連携できるため、AI エージェントのワークフローをつなぐ標準的なツールになっています。1 つの docker run で、2 分以内に使用可能なエディターを構築できます。このガイドでは、残りの 90% を扱います。デフォルトの SQLite ファイルではなく Postgres で永続化し、HTTPS 経由でアクセスできるようにします。そして、ほとんどの人が誤る点として、外部から実際に到達できる URL を Webhook が提供するようにします。

完成した構成では、1 つの Docker ネットワーク上で 2 つのコンテナを稼働させます。n8n 本体と、ワークフローおよび認証情報を保持する Postgres データベースです。ホスト上のリバースプロキシが TLS を終端し、localhost 上の n8n に転送します。そのため、インターネットに直接公開されるのはこのプロキシ経由の接続だけです。他のサービスとともに、2026 年版セルフホスティング候補一覧 に含まれます。

前提条件と、現実的な制約

最低 1 GB の RAM を搭載した VPS が必要です。ワークフローが実際の処理を行うようになったら、2 GB を見込んでください。実行処理と Node.js ランタイムがメモリを消費するためです。実行中に OOM killer がコンテナを停止し、その時点で初めて容量不足に気付くのは避けるべきです。最初は 1 vCPU で問題ありません。

このサーバーでより負荷の高いサービスも実行する場合は、先にそのサービスに合わせて容量を決めてください。一般的な原因はフォトライブラリです。PhotoPrism と Immich に実際に必要な RAM の下限は、n8n が要求する容量を大きく上回ります。

メディアサーバーも同様です。Jellyfin と、そのライブラリを閲覧するためのフロントエンド(ライブラリを 90 年代のレンタルショップのように再構成する Halcyonなど)を実行すると、n8n が気付くよりはるかに早く RAM とトランスコード用の余裕を消費します。

ドメインまたはサブドメインが必要です。ここでは n8n.example.com とします。VPS のパブリック IP を指す A レコードを設定し、証明書を要求する前に名前解決できる状態にしてください。ポート 80 と 443 はプロキシからアクセスできるように開放する必要があります。n8n 自身のポート 5678 はインターネットに公開してはいけません。Docker Engine と Compose plugin も必要です。docker compose version が docker: 'compose' is not a docker command でエラーになる場合は、古い standalone binary を使用しています。plugin は sudo apt install docker-compose-plugin です。

SQLite はテスト用途なら十分ですが、依存する用途には Postgres を使用します

n8n のデフォルトデータベースは /home/node/.n8n/database.sqlite にある SQLite ファイルです。動作確認だけなら問題ありません。ボリュームをマウントしなければ、最初にコンテナを再作成した時点でデータは失われます。これ自体が学ぶべき点です。Postgres に移行する理由は、単純な速度ではありません。SQLite は書き込みロックを 1 つしか保持できないため、複数のワークフローを同時に実行するインスタンスや、将来使うことになるキューモードでは、並行処理時に SQLITE_BUSY: database is locked が発生します。Postgres にはこの上限がなく、pg_dump で適切にバックアップできます。また、依存するサーバーには Postgres を使う前提で n8n 自身のドキュメントが説明されています。後から切り替える場合は手作業でデータを移行する必要があります。このサーバーが重要であれば、最初から Postgres を使用してください。

DNS とファイアウォール

レコードを設定し、先にポートを開放してください。後続の証明書取得で、名前解決できない名前を指定して失敗するのを防げます。

dig +short n8n.example.com
curl -s ifconfig.me
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow OpenSSH
sudo ufw enable

5678 は開放しないでください。compose ファイルは n8n を 127.0.0.1:5678 にバインドするため、ホスト上のリバースプロキシだけが接続できます。ufw allow 5678 を設定すると、この分離が失われます。

Compose ファイル

作業ディレクトリを作成し、docker-compose.yml を配置します。これがスタック全体です。サービスは 2 つ、プライベートネットワークは 1 つ、名前付きボリュームは 2 つです。

services:
  postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_DB: n8n
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - n8n_net
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]
      interval: 10s
      timeout: 5s
      retries: 5

  n8n:
    image: docker.n8n.io/n8nio/n8n:2.29.10
    restart: unless-stopped
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      - N8N_HOST=n8n.example.com
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://n8n.example.com/
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - N8N_PROXY_HOPS=1
      - GENERIC_TIMEZONE=Europe/London
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
    volumes:
      - n8n_data:/home/node/.n8n
    networks:
      - n8n_net
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  postgres_data:
  n8n_data:

networks:
  n8n_net:

明示しておくべき点がいくつかあります。DB_POSTGRESDB_HOST=postgres は サービス名 です。Docker はこれを共有ネットワーク上で解決します。localhost ではありません。n8n コンテナ内では、localhost は n8n 自体を意味します。depends_on と condition: service_healthy により、起動時に n8n が Postgres より先に起動しようとする競合を防ぎます。これらがないと、n8n は起動後にデータベースを見つけられず、終了します。/home/node/.n8n にある名前付きボリューム n8n_data には、暗号化キーが保存されます。SQLite を使用する場合はデータベースも保存されます。ここは絶対に失わないでください。イメージは正確なバージョンに固定し、latest は決して使用しないでください。理由は、この後のアップグレードのセクションで説明します。

シークレットファイル

compose ファイルにパスワードを記述しないでください。compose が自動的に読み込む .env ファイルを同じディレクトリに配置し、実際にランダムな値になるよう生成してください。

printf 'POSTGRES_PASSWORD=%s\n'  "$(openssl rand -hex 24)" >  .env
printf 'N8N_ENCRYPTION_KEY=%s\n' "$(openssl rand -hex 32)" >> .env
chmod 600 .env

N8N_ENCRYPTION_KEY は、ここで最も重要な文字列です。保存されるすべての認証情報は、このキーで暗号化されます。n8n に生成させず、明示的に設定してください。自分で生成した値なら、記録して復元できます。n8n がこのキーで最初の認証情報を暗号化した後に 変更すると、すべての認証情報を復号できなくなります。そのため、今ここで 1 回だけ設定し、この行は二度と変更しないでください。

Webhook の動作を決める環境変数

4 つの変数が、n8n が外部に対して自分自身をどう示すかを制御します。これらを誤ることが、n8n のサポートで最も多い質問です。

  • N8N_HOST は公開ホスト名です。n8n.example.com が該当します。プロキシ配下でデフォルトの localhost のままにすると、エディターは ブラウザー内で localhost から自身の API を読み込もうとするため、失敗します。
  • N8N_PROTOCOL=https は、TLS 経由で提供されていることを n8n に伝えます。そのため、セッション Cookie に Secure を付け、https:// URL を生成します。
  • N8N_PORT=5678 は、n8n が コンテナ内で 待ち受けるポートです。公開ポートではありません。443 はプロキシが担当します。
  • WEBHOOK_URL=https://n8n.example.com/ が問題になりやすい変数です。n8n は、これらの値から Webhook アドレスを生成します。そのアドレスを Stripe、GitHub、その他の外部呼び出し元に貼り付けます。未設定または誤っている場合、n8n は N8N_HOST:N8N_PORT にフォールバックし、https://n8n.example.com:5678/webhook/...、またはさらに悪い場合は http://localhost:5678/webhook/... を表示します。エラーは表示されません。もっともらしく見える一方でインターネットから到達できないため、呼び出し元からのリクエストは何も起きないまま届きません。末尾のスラッシュを含む正確な公開ベース URL を設定し、Webhook ノードにポートなしの URL が表示されることを確認します。

N8N_PROXY_HOPS=1 は、n8n の Express サーバーに対して、手前に 1 台のプロキシがあることを信頼するよう指示します。これにより、レート制限やクライアント IP を参照する機能が、プロキシのアドレスではなく実際のアドレスを認識できます。ここで意図的に設定しない変数は N8N_RUNNERS_ENABLED です。Task runners は、n8n が Code ノードのロジックをメインプロセスから分離された別プロセスで実行する機能です。1.x 系では一貫してオプトイン方式で、変数のデフォルトは false であり、true に設定する必要がありました。n8n 2.0 ではデフォルトで有効になったため、2.x 系ではこのガイドは変数を固定しません。この変数は非推奨であり、n8n のドキュメントでも、もはや設定する必要はないと説明されています。

初回起動

docker compose up -d
docker compose ps
docker compose logs -f n8n

正常な初回起動では、n8n ready on ..., port 5678 の行に続いて Editor is now accessible via: の行が表示されます。docker compose ps には Up の両方のコンテナが表示され、postgres は (healthy) とマークされている必要があります。n8n が Restarting ループに陥っている場合はログを確認してください。ほとんどの場合、原因は後述するデータベース接続またはボリュームの権限です。

リバースプロキシでの TLS

n8n 自体は 5678 でプレーン HTTP を使用します。HTTPS の終端は前段のコンポーネントが担います。構成は 2 つあります。

すでに複数のコンテナを運用している場合は、TLS 証明書を自動発行する Traefik リバースプロキシの背後に n8n を配置し、いくつかのラベルを設定します。Traefik が証明書の取得と更新を行います。

このサーバーで運用するアプリが n8n だけであれば、Let's Encrypt 証明書を使用する nginx のバーチャルホスト構成が簡単です。証明書を取得するには、Ubuntu 24.04 で Certbot と nginx の TLS を設定する手順を使用します。その後、次の server ブロックを設定します。

server {
    listen 443 ssl;
    server_name n8n.example.com;

    ssl_certificate     /etc/letsencrypt/live/n8n.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 3600;
        client_max_body_size 16m;
    }
}

Upgrade と Connection "upgrade" のヘッダーは省略できません。n8n は WebSocket 経由でエディターに実行状況の更新をリアルタイムで送信します。この 2 行がないと、ログインページの読み込み後に接続が失われたことを示すバナーが表示され、処理が停止します。proxy_read_timeout 3600 により、長時間実行される処理が nginx のデフォルト設定である 60 秒後に切断されるのを防ぎます。X-Forwarded-Proto $scheme ヘッダーは N8N_PROXY_HOPS=1 と組み合わせて使用します。プロキシから n8n への接続がプレーン HTTP であっても、元のリクエストが HTTPS だったことを n8n に伝えます。これにより、n8n は接続が安全でないと判断して自身の Cookie を拒否しません。

最初のワークフローを実際に動かす

https://n8n.example.com/を開き、所有者アカウントを作成します(次のセクションを参照)。そのうえで、経路が機能することを確認できる最小構成を作成します。Webhook を受信し、HTTP リクエストを実行して、レスポンスを返す構成です。

  1. Webhook ノードを追加します。メソッドを POST、パスを hello のように設定します。ここには Test URL と Production URL の2つの URL が表示されます。これは、「Webhook が動作しない」という報告の主な原因です。Test URL は、Listen for test event をクリックしている間に限り、1回の呼び出しに応答します。その後は期限切れになります。Production URL は、ワークフローを publish した時点で登録され、それ以降の呼び出しに応答します。
  2. HTTP Request ノードを追加し、前のノードに接続します。任意の公開 JSON API を指定します。https://api.github.com/zen への GET リクエストは1行の文字列を返すため、これで十分です。
  3. Respond to Webhook ノードを追加します。Webhook ノードの Respond オプションを「Using Respond to Webhook node」に設定し、呼び出し元に HTTP ノードの出力を返すようにします。
  4. 右上の Publish をクリックし、curl -X POST https://n8n.example.com/webhook/hello を呼び出します。zen の文言が返れば成功です。POST を受信し、API を呼び出し、レスポンスを返すという、実際の自動化の多くで使われる構成です。古いガイドでは、ここで Active の切り替えを有効にするよう説明されています。n8n 2.0 では、この切り替えが Publish と Unpublish に置き換えられました。

スケジュール実行にする場合は、Webhook ノードを Schedule Trigger に置き換え、代わりにモデルのエンドポイントを呼び出します。同じ VPS 上で Ollama を実行する構成 は、毎晩実行する要約処理を構築する方法として扱いやすい選択肢です。

ユーザー管理、Basic 認証ではありません

以前の n8n ガイドでは、N8N_BASIC_AUTH_ACTIVE=true を設定するよう案内していました。これらの変数は n8n 1.0 で削除され、現在は何も行いません。現在の認証は owner account です。エディターを初めて開くと、n8n でメールアドレスとパスワードを使う owner account を作成します。この認証ゲートは必須で、匿名モードはありません。初回起動後、URL を他の人に渡す前に、すぐ作成してください。docker compose up から最初のフォーム送信までの間は、最初にアクセスしたユーザーがインスタンスを取得できます。リバースプロキシに basic-auth 層を追加するのは、追加のロックとして妥当です。ただし、これは第 2 の認証要素であり、本来の認証ではありません。このガイドで扱う owner account とその他の機能は、無料の community edition で利用できます。後から細かなロールを持つ追加ユーザーや SSO が必要になる場合は、計画を立てる前に 有料ライセンスが必要な n8n の機能 を確認してください。

バックアップでは、最初に暗号鍵、次にデータベースを確保します

バックアップが必要なものは2つあります。ただし、同じように置き換えられるわけではありません。

N8N_ENCRYPTION_KEYです。 n8n に保存するすべての認証情報、API トークン、データベースパスワード、OAuth シークレットは、この鍵で保存時に暗号化されます。Postgres 内のワークフローは、この鍵がなければ使用できません。別の鍵を使う新しいサーバーにデータベースを復元しても、n8n は認証情報を1つも復号できず、復旧もリセットもできません。.env ファイルにこの鍵が保存されています。作成した当日にサーバー外へコピーしてください。パスワードマネージャーのエントリに保存する方法が適しています。実際に重要なのは、このバックアップです。

Postgres データベースもバックアップします。データベースには、ワークフロー、実行履歴、暗号化された認証情報そのものが保存されています。

docker compose exec -T postgres pg_dump -U n8n -d n8n \
  | gzip > n8n-db-$(date +%F).sql.gz

これをスケジュールに従って実行し、ダンプをサーバー外へコピーしてください。新しい VPS に復元する場合は、まずスタックを1回起動してデータベースを作成し、n8n を停止します。psql でダンプを読み込み、同じ N8N_ENCRYPTION_KEY を .env に設定してから、n8n を起動してください。同じ鍵とダンプがあれば動作するインスタンスを復元できます。新しい鍵を使うと、どの認証情報も利用できないワークフローになります。

アップグレード: タグを固定する

compose ファイルでは、意図的に latest ではなく n8nio/n8n:2.29.10 を固定しています。n8n はほとんどの週で新しいマイナーバージョンをリリースし、バージョン間でデータベーススキーマやノードの動作が変わることもあります。そのため、latest のままでは、更新されていない pull によって、起動直後にデータベースを移行するビルドが適用される可能性があります。バージョンを固定し、更新前に リリースノート を確認してください。n8n はそこで破壊的変更を明記しています。次の手順で、意図的にアップグレードします。

docker compose exec -T postgres pg_dump -U n8n -d n8n | gzip > pre-upgrade.sql.gz
# edit the image tag in docker-compose.yml, then:
docker compose pull n8n
docker compose up -d n8n
docker compose logs -f n8n

メジャーバージョンをまたぐ更新では、これが特に重要です。たとえば 2.0 系では、デフォルトで N8N_BLOCK_ENV_ACCESS_IN_NODE から true に変更されました。そのため、process.env を読み取る Code ノードは、false に戻すまでアクセスできなくなります。同じリリースでは、settings ファイルに対する厳格な権限チェックが適用され、task runners がデフォルトで有効になり、workflow の Activate トグルが Publish に置き換えられました。メジャーバージョンの境界を越える前に、2.0 の破壊的変更ページを確認してください。n8n は起動時に必要なデータベース移行を自動的に実行します。だからこそ、アップグレード前の pg_dump は省略できません。認証情報は .env のキーで暗号化され、データは Postgres に保存されるため、コンテナは使い捨てにできます。タグを以前のものに固定して dump を復元すれば、ロールバックできます。

失敗のパターンと表示されるメッセージ

The requested webhook "POST hello" is not registered. 公開されていないワークフローの webhook を呼び出した場合、または誰も待ち受けていないテストパスを呼び出した場合は、404 が返ります。テストパス(/webhook-test/...)は、「Listen for test event」をクリックしている間だけ応答します。本番パス(/webhook/...)は、ワークフローを公開した後だけ応答します。関連する This webhook is not registered for GET requests. Did you mean to make a POST request? はメソッドが正しくないことを示します。ノードは POST を想定していますが、GET を送信しています。

Webhook URL に :5678 または localhost が表示される。 ノードには https://n8n.example.com:5678/webhook/... または http://localhost:5678/... が表示されます。WEBHOOK_URL が未設定または正しくないため、n8n は公開ベース URL ではなく N8N_HOST:N8N_PORT からアドレスを組み立てています。WEBHOOK_URL=https://n8n.example.com/ を設定し、docker compose up -d を使ってコンテナを再作成すると、ポート表示が消えます。

ブラウザーに There was a problem loading init data が表示される。エディターは読み込まれていますが、自身のバックエンド API に接続できていません。プロキシの背後では、ほとんどの場合、N8N_HOST または WEBHOOK_URL が正しくないか、プロキシに WebSocket 用の Upgrade ヘッダーがないか、N8N_PROTOCOL が接続方法と一致していません。外部公開用の4つの変数を確認し、プロキシが Upgrade と Connection を転送していることを確認してください。

コンテナが再起動を繰り返し、ログに password authentication failed for user "n8n" が表示される。n8n が送信するパスワードと、データベースの初期化時に設定されたパスワードが一致していません。注意点は、Postgres が POSTGRES_PASSWORD を読み取るのは、空のデータディレクトリを初期化するときだけだということです。スタックを一度起動した後、.env 内の POSTGRES_PASSWORD を変更しても、既存の postgres_data ボリュームには古いパスワードが残っています。元のパスワードに戻してください。保持するデータがない場合に限り、docker compose down と docker volume rm を実行して postgres ボリュームを削除し、初期状態で起動し直します。

起動時に EACCES: permission denied, open '/home/node/.n8n/config' が表示される。n8n は node ユーザー(UID 1000)として実行されるため、設定ディレクトリに書き込めません。root が所有するホストディレクトリ(./n8n_data:/home/node/.n8n)を bind mount した場合に発生します。上記の named volume を使用してください。bind mount を使用する場合は、先に sudo chown -R 1000:1000 ./n8n_data を実行してください。

Permissions 0644 for n8n settings file /home/node/.n8n/config are too wide. Changing permissions to 0600.. 2.x 系では、n8n はデフォルトでその設定ファイルに 0600 を適用し、起動時に自動修正します。このログ行は、モードがすでに修正されたことを示します。bind mount の後や、権限が緩い状態でファイルを復元した後によく発生します。対応は不要です。ファイルシステムが本当に権限をサポートできない場合に限り、N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=false を設定してください。

Mismatching encryption keys 詳細なログ行には、設定ファイル内の暗号化キー /home/node/.n8n/config が、環境変数内の N8N_ENCRYPTION_KEY と一致しないと表示されます。環境変数に設定されたキーが、以前の実行時に n8n がデータボリュームへ書き込んだキーと異なっています。多くの場合、以前の起動時に変数が未設定だったため n8n がランダムなキーを生成し、その後で別のキーを設定したことが原因です。.env に元のキーを設定してください。保存する価値のある認証情報が本当にない場合に限り、n8n_data ボリューム内の config ファイルを削除して n8n に再生成させます。この場合、既存の認証情報は読み取れなくなります。

安全な cookie に関するログインバナー: Your n8n server is configured to use a secure cookie, however you are either visiting this via an insecure URL, or using Safari. N8N_PROTOCOL=https を設定しているのに、平文の HTTP で n8n にアクセスしています。通常は HTTPS プロキシではなく、IP アドレスとポートへ直接アクセスした場合に発生します。https://n8n.example.com/ 経由でアクセスしてください。HTTPS を本当に使用できない場合に限り N8N_SECURE_COOKIE=false を設定してください。インターネットに公開するサーバーでは設定しないでください。

これらのワークフローに言語モデルを組み込む方法は、Claude と n8n で AI ワークフローを構築するを参照してください。

FAQ

n8n では SQLite と Postgres のどちらを使うべきですか?

SQLite(デフォルト)は、n8n を試す場合や、一度に 1 つのワークフローだけを実行する個人用インスタンスには適しています。依存する用途には Postgres を使用してください。SQLite の単一ライター用ロックは並行実行時に database is locked を発生させますが、Postgres なら pg_dump で確実にバックアップできます。後からの移行は手作業になるため、重要なサーバーであれば最初から Postgres を使用します。

n8n の Webhook がまったく実行されないのはなぜですか?

ほとんどの場合、原因は WEBHOOK_URL です。未設定または誤って設定されていると、n8n は N8N_HOST:N8N_PORT から構成した Webhook アドレスを表示します。そこには :5678 や localhost が含まれることが多く、一見有効でもインターネットから到達できないため、呼び出し元のリクエストが届きません。WEBHOOK_URL=https://n8n.example.com/ を設定し、ノードにポート番号のない URL が表示されることを確認します。もう 1 つの原因は、公開していないワークフローの本番 URL を呼び出していることです。この場合は The requested webhook ... is not registered. が返されます。

n8n で何をバックアップする必要がありますか?

2 つあります。1 つ目は .env ファイルに含まれる N8N_ENCRYPTION_KEY です。保存されているすべての認証情報はこれで暗号化されているため、失うと永久に復号できなくなります。作成した当日にサーバー外へコピーしてください。2 つ目は、ワークフロー、履歴、認証情報を保存する Postgres データベースの pg_dump です。復元には、同じキーとダンプの両方が必要です。

n8n を HTTPS の背後に配置するにはどうすればよいですか?

n8n はポート 5678 で平文の HTTP を提供します。前段のリバースプロキシで TLS を終端します。n8n を 127.0.0.1:5678 にバインドしてプロキシだけが接続できるようにし、Traefik で自動証明書を使用するか、nginx で Let's Encrypt 証明書を使用します。N8N_PROTOCOL=https と WEBHOOK_URL=https://your-host/ を設定し、プロキシが WebSocket の Upgrade ヘッダーを転送することを確認します。転送されないとエディターが停止します。

n8n を安全にアップグレードするにはどうすればよいですか?

latest ではなく、特定のイメージタグを固定します。n8n は起動時に自動的にマイグレーションを実行するため、最初に pg_dump を取得します。破壊的変更についてリリースノートを確認し、タグを更新して docker compose pull n8n && docker compose up -d n8n を実行します。コンテナは使い捨てであるため、以前のタグを固定し、アップグレード前のダンプを復元すればロールバックできます。