
VPSにn8nを立てたら、Dockerで3回ハマった話 ―Kubernetesのingressまで試して分かったこと
セルフホスト型のワークフロー自動化ツール「n8n」を自分のVPSに立てようとした週末、気づいたら丸2日が溶けていた。
「Dockerで簡単に動かせます」という公式ドキュメントの一文を信じてターミナルを開いたものの、実際にやってみるとコンテナは起動しない、リバースプロキシの設定は噛み合わない、データは消える……と、次から次へと壁が出てきた。同じようなところで詰まっている人が絶対いるはずだと思って、この記事を書いた。
Dockerで躓いた3つのポイントと、番外編としてKubernetes(k3s)+Ingressパターンまで試した結果をまとめている。どちらを選ぶか迷っている人にも参考になると思う。
────────────
目次
この記事で扱うこと・扱わないこと
n8nをDockerで立てる基本構成の確認
ハマりポイント①:パーミッションエラーでコンテナが即死する
ハマりポイント②:WebhookのURLが内部アドレスになってしまう
ハマりポイント③:NginxのリバースプロキシでWebSocketが切れる
データ永続化の落とし穴:「あれ、ワークフローが消えた」を防ぐ
Kubernetes(k3s)でn8nを動かす ―Ingressパターンの詳細
DockerComposeとk3s、結局どちらを選ぶか
まとめ
この記事で扱うこと・扱わないこと
扱うこと
VPS(Ubuntu 22.04想定)でDockerを使ってn8nを立てるときにハマりやすいポイント
docker-compose構成のベストプラクティス
Nginx(またはTraefik)をリバースプロキシにする設定の注意点
k3s+Ingress Nginxでn8nをデプロイするパターンとトレードオフ
扱わないこと
n8nの使い方・ワークフローの作り方
AWS EKSやGKEなどのマネージドKubernetes
使っているVPSはConoHaとVultrを行き来しているが、どちらも基本的に同じ手順で動く。スペックの目安はvCPU 2コア、メモリ2GB以上あれば快適に動いた。
────────────
n8nをDockerで立てる基本構成の確認
まず前提として、n8nのDockerイメージはDockerHubで公開されている(n8nio/n8n)。公式ドキュメントにはdocker-composeの最小構成が載っているが、あれをそのままコピペすると後で詰まる部分がある。
最小限の構成はこうなる。
version: "3.8"
services:
n8n:
image: n8nio/n8n
restart: always
ports:
- "5678:5678"
environment:
- N8N_HOST=your-domain.example.com
- N8N_PORT=5678
- N8N_PROTOCOL=https
- WEBHOOK_URL=https://your-domain.example.com/
volumes:
- n8n_data:/home/node/.n8n
volumes:
n8n_data:
一見シンプルに見える。でもここに落とし穴がいくつか潜んでいる。
────────────
ハマりポイント①:パーミッションエラーでコンテナが即死する
最初のハマりはこれだった。コンテナを起動すると数秒で落ちて、ログを見ると次のようなエラーが出ていた。
Error: EACCES: permission denied, mkdir '/home/node/.n8n'
原因
Dockerのnamed volumeではなく、ホストのディレクトリをマウントしているときによく起きる。ホストのディレクトリのオーナーがrootになっていて、コンテナ内のnodeユーザー(UID 1000)が書き込めない。
解決策
named volumeを使う場合は基本的に問題が起きにくいが、ホストディレクトリをマウントしたい(バックアップしやすくしたいなど)場合は事前にオーナーを変えておく。
mkdir -p /opt/n8n/data
chown -R 1000:1000 /opt/n8n/data
そしてcomposeのvolumes設定をこう書き換える。
volumes:
- /opt/n8n/data:/home/node/.n8n
これだけで即死ループから脱出できた。
『(ここに体験:「restart: always」のせいで気づかないまま再起動ループしていてdocker logsを見るまで理由が分からなかった話)』
ちなみにrestart: alwaysが設定してあるとコンテナが何度も再起動し続けるので、docker logs -f n8nでリアルタイムにログを追いかけるのが一番早い。docker compose psでSTATUSを見るだけだと「起動してる」ように見えてしまうことがある。
────────────
ハマりポイント②:WebhookのURLが内部アドレスになってしまう
n8nのワークフローでWebhookトリガーを使おうとすると、生成されるURLがhttp://localhost:5678/webhook/...になってしまう問題。外部サービス(SlackやGitHubなど)からのWebhookが受け取れない。
原因
環境変数WEBHOOK_URLが正しく設定されていないか、設定されていてもN8N_PROTOCOLと噛み合っていないことが多い。
必要な環境変数の整理
変数名 / 設定すべき値の例 / 役割
N8N_HOST … 設定すべき値の例:your-domain.example.com / 役割:n8nが認識するホスト名
N8N_PORT … 設定すべき値の例:5678 / 役割:n8nが使うポート
N8N_PROTOCOL … 設定すべき値の例:https / 役割:httpかhttpsか
WEBHOOK_URL … 設定すべき値の例:https://your-domain.example.com/ / 役割:Webhook URLのベース
末尾のスラッシュを忘れやすいので注意。WEBHOOK_URLの末尾にスラッシュがないと、Webhookのパスが正しく生成されない。
────────────
ここまでが無料で読める部分だ。次のセクションからは、リバースプロキシとの組み合わせで起きたSSL/HTTPSまわりの問題、データ永続化に関するよくある誤解、そしてKubernetes(k3s)でのデプロイパターンとIngressの設定を詳しく書いている。