n8nは自分のサーバーに入れて動かせます。ここでは手元のPCで試す方法と、月額1,000〜2,000円台のVPSにDocker Composeで本番を立て、n8n.example.co.jp のような独自ドメインとHTTPSで使うまでを順に書きます。読み終わると、問い合わせフォームからのWebhookを受けられる本番のn8nが1台立ち、暗号鍵・タイムゾーン・バックアップ・証明書の4つで後から困らない状態になります。前提はLinuxのサーバーにSSHで入ってコマンドを打てることです。版は執筆時点の安定版 2.44.7 で確かめています。
先に決めること
手元で試すだけか、本番で使うか
手元のn8nはPCを閉じれば止まり、その間はスケジュールもWebhookも動きません。操作を覚える段階なら手元で十分です。「問い合わせ受付」ワークフローのように業務を載せるなら、最初から24時間動くサーバーに立てます。試すだけならn8n Cloudのトライアルを使う手もあり、費用の比べ方は「n8nの料金:Cloudとセルフホストの費用、商用利用の線引き」に書いています。
データベースはSQLiteかPostgreSQLか
何も設定しなければSQLiteです。ワークフロー・Credential(接続情報)・実行履歴が database.sqlite という1つのファイルに入ります。公式は、試すにはSQLiteで十分、数人以上で使う・一日中ワークフローが動く本番ではPostgreSQLを、と勧めています。
社員30名ほどで、ワークフロー10本前後・触る人2〜3人ならSQLiteでも回ります。ただし後から替えると書き出して入れ直す手間が出るので、触る人が増える見込みがあるなら最初からPostgreSQLにします。この記事はSQLiteで立て、応用で切り替え方を書きます。
サーバーの大きさ
公式はn8nはCPUよりメモリが効くとし、メモリ320MB〜2GB、DBは512MB〜4GBのSSDという例を示しています。問い合わせ受付のような軽い処理ならメモリ2GBのVPSで足ります。
一方、n8n Assistant(組み込みのAIの手伝い機能)用のサンドボックスまで載せる公式の新しいCompose構成は、メモリ4GB・2vCPU以上が要ります。安いVPSでは、この記事のTraefik版で本体だけを立てます。WordPress用の共用レンタルサーバーはDockerが動かないので使えません。AWSのEC2でも手順は同じで、ファイアウォールがセキュリティグループという別画面にあるのが違いです。
独自ドメインとHTTPSが要る理由
1つ目はWebhookです。フォームやSlackからn8nを呼んでもらうには、インターネットから届くURLが要ります。n8nはそのURLをWebhookノードに表示し、外のサービスにも登録するので、ここがlocalhostだと外からは届きません。
2つ目はOAuthのコールバックです。GoogleスプレッドシートやGmailのCredentialでは、認証後の戻り先(OAuth Redirect URL)をGoogle側に登録します。手元なら http://localhost:5678/rest/oauth2-credential/callback で通りますが、本番のURLに移ると登録し直しです。最初から本番のURLで作れば1回で済みます。
URLはサブドメインにします。example.co.jp/n8n/ のようなパスはリバースプロキシと相性が悪いと公式にあります。
立て方と環境変数の一覧
| 立て方 | 向いている場面 | 外からのWebhook |
|---|---|---|
npx n8n | 手元で今すぐ触る。Node.js 24系が必要 | 届かない |
docker run | 手元で試し、データも残す | 届かない |
| Docker Compose+Traefik | VPSの本番。証明書の更新も自動 | 届く |
| Docker Compose+nginx+certbot | 既にnginxが動いているVPS | 届く |
本番で押さえる環境変数です。
| 環境変数 | 例の値 | 意味 |
|---|---|---|
N8N_HOST | n8n.example.co.jp | ホスト名。既定は localhost |
N8N_PROTOCOL | https | 外から見たプロトコル。既定は http |
N8N_WEBHOOK_URL | https://n8n.example.co.jp/ | Webhookの外向きURL。旧名 WEBHOOK_URL は2.35.0から非推奨 |
N8N_PROXY_HOPS | 1 | 前に立つリバースプロキシの数 |
N8N_EDITOR_BASE_URL | https://n8n.example.co.jp/ | 編集画面の公開URL。n8nが送るメールにも使う |
GENERIC_TIMEZONE | Asia/Tokyo | n8nのタイムゾーン。既定は America/New_York |
TZ | Asia/Tokyo | コンテナのOSの時刻 |
N8N_ENCRYPTION_KEY | ランダムな値 | Credentialの暗号鍵。未設定なら初回起動時に自動生成 |
DB_TYPE | postgresdb | PostgreSQLを使うとき。未設定ならSQLite |
N8N_RUNNERS_MODE | external | Codeノードを別コンテナで動かす |
N8N_RUNNERS_AUTH_TOKEN | ランダムな値 | n8nと実行用コンテナの合言葉 |
手元のPCで試す
npxで動かす
n8n 2.44.7 のパッケージが求めるNode.jsは24以上です(公式ドキュメントの記載は20.19〜24.x なので、24系を入れます)。
- Node.jsの24系を入れる
node -vがv24で始まることを確かめるnpx n8nを実行する- ブラウザで
http://localhost:5678を開き、オーナーアカウントを作る
npmで入れたn8nではn8n Assistantが使えず、npmでの導入は3.0で廃止予定です。長く使うならDockerにします。
docker runで動かす
Mac・WindowsはDocker Desktopを入れます。WindowsはWSL2の上で動かし、作業フォルダは /mnt/c/ の下ではなくWSLの中に置きます。
docker volume create n8n_data
docker run -it --rm \
--name n8n \
-p 5678:5678 \
-e GENERIC_TIMEZONE="Asia/Tokyo" \
-e TZ="Asia/Tokyo" \
-e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \
-v n8n_data:/home/node/.n8n \
n8nio/n8n:2.44.7
データは n8n_data ボリュームに残り、同じコマンドで続きから使えます。GoogleはOAuthの戻り先にlocalhostを認めるので、スプレッドシートとつなぐ練習もできます。

VPSに本番を立てる
公式のDocker Compose+Traefik版に本番用の設定を足した形で書きます。Traefikは証明書の取得と更新を自動でやるリバースプロキシです。
1. VPSを借りてファイアウォールを開ける
- メモリ2GB以上のVPSをUbuntuで借りる
- SSHの鍵を登録し、IPアドレスを控える
- VPSのファイアウォールで22番・80番・443番を開ける
- 5678番は開けない
2. DockerとDocker Composeを入れる
Dockerの公式手順どおりに入れ、確かめます。sudo なしで使うなら作業ユーザーを docker グループに入れます。
docker --version
docker compose version
sudo usermod -aG docker ${USER}
exec sg docker newgrp
3. DNSにAレコードを足す
ドメインを買ったところの管理画面で、Aレコードを足します。
| 種類 | 名前 | 値 |
|---|---|---|
| A | n8n | VPSのIPアドレス |
dig +short n8n.example.co.jp でVPSのIPが返るまで待ちます。反映前に起動すると、Traefikの証明書の取得が失敗します。
4. 作業フォルダと .env を作る
mkdir n8n-compose
cd n8n-compose
mkdir local-files
openssl rand -hex 32
openssl rand -hex 32
local-files はファイルをやり取りする場所で、権限を正しくするため手で作ります。openssl の2つの値は暗号鍵と合言葉に使います。.env は次のとおりです。GENERIC_TIMEZONE を書かないとニューヨーク時間になり、SSL_EMAIL には証明書の案内が届きます。
DOMAIN_NAME=example.co.jp
SUBDOMAIN=n8n
GENERIC_TIMEZONE=Asia/Tokyo
SSL_EMAIL=admin@example.co.jp
N8N_ENCRYPTION_KEY=1つ目の値
N8N_RUNNERS_AUTH_TOKEN=2つ目の値
鍵が入るので chmod 600 .env で権限を絞り、Gitには入れません。
5. compose.yaml を作る
公式のTraefik版に、本番で要るものを # 追加 の印付きで足しています。n8nの版は 2.44.7 に固定し、Codeノード実行用の n8nio/runners も同じ版にそろえます(公式の決まりです)。
services:
traefik:
image: "traefik"
restart: always
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.mytlschallenge.acme.tlschallenge=true"
- "--certificatesresolvers.mytlschallenge.acme.email=${SSL_EMAIL}"
- "--certificatesresolvers.mytlschallenge.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
volumes:
- traefik_data:/letsencrypt
- /var/run/docker.sock:/var/run/docker.sock:ro
n8n:
image: n8nio/n8n:2.44.7
restart: always
ports:
- "127.0.0.1:5678:5678"
labels:
- traefik.enable=true
- traefik.http.routers.n8n.rule=Host(`${SUBDOMAIN}.${DOMAIN_NAME}`)
- traefik.http.routers.n8n.tls=true
- traefik.http.routers.n8n.entrypoints=web,websecure
- traefik.http.routers.n8n.tls.certresolver=mytlschallenge
- traefik.http.middlewares.n8n.headers.SSLRedirect=true
- traefik.http.middlewares.n8n.headers.STSSeconds=315360000
- traefik.http.middlewares.n8n.headers.browserXSSFilter=true
- traefik.http.middlewares.n8n.headers.contentTypeNosniff=true
- traefik.http.middlewares.n8n.headers.forceSTSHeader=true
- traefik.http.middlewares.n8n.headers.SSLHost=${DOMAIN_NAME}
- traefik.http.middlewares.n8n.headers.STSIncludeSubdomains=true
- traefik.http.middlewares.n8n.headers.STSPreload=true
- traefik.http.routers.n8n.middlewares=n8n@docker
environment:
- N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true
- N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME}
- N8N_PORT=5678
- N8N_PROTOCOL=https
- NODE_ENV=production
- N8N_WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/
- N8N_EDITOR_BASE_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/ # 追加
- N8N_PROXY_HOPS=1 # 追加
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- TZ=${GENERIC_TIMEZONE}
- N8N_RESTRICT_FILE_ACCESS_TO=/files
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY} # 追加
- N8N_RUNNERS_MODE=external # 追加
- N8N_RUNNERS_BROKER_LISTEN_ADDRESS=0.0.0.0 # 追加
- N8N_RUNNERS_AUTH_TOKEN=${N8N_RUNNERS_AUTH_TOKEN} # 追加
volumes:
- n8n_data:/home/node/.n8n
- ./local-files:/files
task-runners: # 追加
image: n8nio/runners:2.44.7
restart: always
environment:
- N8N_RUNNERS_TASK_BROKER_URI=http://n8n:5679
- N8N_RUNNERS_AUTH_TOKEN=${N8N_RUNNERS_AUTH_TOKEN}
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE} # 追加
- TZ=${GENERIC_TIMEZONE} # 追加
depends_on:
- n8n
volumes:
n8n_data:
traefik_data:
N8N_PROXY_HOPS は公式がリバースプロキシの後ろでは 1 にするよう書いている設定です。task-runners は、Codeノードのコードをn8n本体と別のコンテナで動かすためのものです。公式は、分けない内部モードだとワークフローを編集できる人が暗号鍵やCredentialまで読めるおそれがあるとして、本番では外部モードを勧めています。Codeノードはこの task-runners 側で動くので、タイムゾーンもこちらに渡します。n8n本体にだけ書くと、Codeノードの中の $now はニューヨーク時間、new Date() は世界標準時のままです。
6. 起動する
sudo docker compose up -d
sudo docker compose ps
sudo docker compose logs -f traefik
3つのコンテナが running になれば起動しています。
7. オーナーアカウントを作る
https://n8n.example.co.jpを開き、鍵のマークが出ていることを確かめる- メールアドレス・名前・パスワードを入れる(8文字以上、数字と大文字を1つ以上)
- 「Next」(次へ)を押す
最初にこの画面を通った人がオーナーになるので、起動したらすぐ作ります。社員の招待は、左下のユーザーアイコン横の「…」→「Settings」→「Users」→「Invite」です。SMTPを設定しないと招待リンクは手で渡すことになり、パスワードの再設定もできません。
8. 外から届くかを確かめる
- 新しいワークフローにWebhookノードを置く
- ノードを開き、「Webhook URLs」の「Production URL」タブに出るURL(本番用URL)が
https://n8n.example.co.jp/webhook/で始まることを確かめる curl -s https://n8n.example.co.jp/healthzが応答することを確かめる

nginx+certbotで立てる別解
既にnginxが動いているVPSなら、compose.yaml から traefik と labels を外し、nginxを前に立てます。n8nは画面の更新にWebSocketを使うので、Upgrade を転送しないと画面が固まります。
server {
listen 443 ssl;
server_name n8n.example.co.jp;
ssl_certificate /etc/letsencrypt/live/n8n.example.co.jp/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/n8n.example.co.jp/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-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
この形の弱点は、証明書の更新を自分で仕込まないと誰も更新しないことです。取るときに更新後のnginx読み直しを指定し、予行演習まで通します。
sudo certbot certonly --webroot -w /var/www/html -d n8n.example.co.jp --deploy-hook "systemctl reload nginx"
sudo certbot renew --dry-run
立てたら最初にやること
暗号鍵を控える
n8nはCredentialを暗号鍵で暗号化してDBに保存します。鍵をなくすと、DBのバックアップがあってもCredentialは復号できず、スプレッドシート・Gmail・Slackの接続を全部作り直しです。この手順では鍵を .env に書いたので、.env をサーバーの外に控えます。鍵を指定せずに立てた環境では、自動で作られた鍵がボリュームの .n8n/config に入っているので、それを控えます。後から N8N_ENCRYPTION_KEY を足すときは、config と同じ値を入れます。
タイムゾーンを日本にする
GENERIC_TIMEZONE がないとn8nはニューヨーク時間で動き、「毎朝9時に未対応件数をSlackへ」が日本時間の夜22時か23時に届きます。TZ はコンテナのOSの時刻で、ログや date の結果が日本時間になります。上の構成ではCodeノードは task-runners で動くため、そちらにも GENERIC_TIMEZONE と TZ が要ります。2.44.7で試すと、ランナーに渡さない場合はCodeノードの $now がニューヨーク時間、new Date() が世界標準時になり、ランナーに足すとどちらも日本時間に揃いました。ワークフローごとにも、画面上部のワークフロー名の横の「…」から「Settings」を開き、「Timezone」で上書きでき、そちらが優先されます。

バックアップを取る
SQLiteなら、ボリュームの .n8n フォルダにDB・暗号鍵・設定が全部入っています。公式は書き込み中のコピーは壊れうるので、止めてからコピーするよう書いています。
sudo docker compose stop n8n
sudo docker run --rm -v n8n-compose_n8n_data:/data:ro -v $(pwd)/backup:/backup alpine tar czf /backup/n8n-$(date +%Y%m%d).tar.gz -C /data .
sudo docker compose start n8n
ボリューム名の頭には作業フォルダ名が付くので、docker volume ls で確かめます。
実際に本番で動かしていて、2つ困りました。1つは、DBファイルだけをコピーしたら直近の変更が抜けていたことです。2.xのSQLiteは既定でWALモードで動き(2.44.7で database.sqlite-wal と -shm ができるのを確かめています)、本体に書き戻される前の変更が横の database.sqlite-wal にあるため、本体だけを開くと数分前に直したワークフローが古いままでした。-wal -shm も一緒に、上のようにフォルダごと固めれば抜けません。
もう1つは、実行履歴が溜まってDBが1.3GBほどになった環境で、docker cp での取り出しに10分以上かかり、別の日には1時間以上進まずに止まったことです。自動で回すコピーには timeout 1200 のような打ち切りを付けています。DBを太らせないよう、履歴を残す期間と件数も絞ります。
- EXECUTIONS_DATA_MAX_AGE=168
- EXECUTIONS_DATA_PRUNE_MAX_COUNT=10000
履歴の削除は既定で有効(14日・10,000件)ですが、SQLiteのファイルは縮みません。縮めるには DB_SQLITE_VACUUM_ON_STARTUP=true を入れ、業務時間外に再起動します。
証明書の期限を外から見張る
nginx+certbotで立てていた本番で、証明書の期限が切れ、n8nからの通知が全部止まったことがあります。最初の発行をコマンド1回で済ませ、更新を仕込んでいませんでした。毎朝nginxを読み直す設定だけがあり、更新されているつもりになっていました。
通知はn8n自身のWebhookをHTTPSで呼ぶ形だったので、期限切れの瞬間に全部失敗し、同じ経路のエラー通知も出ませんでした。処理自体は成功していましたが、気づくまで数日かかりました。
直したのは3つです。更新とnginxの読み直しを週2回のcronで回す。出力をログに残す。残り日数をサーバーの外の別のPCから毎朝確かめ、30日を切ったら知らせる。監視をn8nの中に置くと、n8nと一緒に止まります。
echo | openssl s_client -connect n8n.example.co.jp:443 -servername n8n.example.co.jp 2>/dev/null | openssl x509 -noout -enddate
certbotの本番の更新は5分以上かかることがあり、途中で止めたらロックが残って以降のcertbotが黙って固まりました。
アップデートの基本
compose.yaml のn8nとrunnersの版を同じ番号に書き換え、sudo docker compose pull、sudo docker compose down、sudo docker compose up -d の順に打ちます。公式は月1回以上の更新とリリースノートの確認を勧めています。戻し方や脆弱性の追い方は「n8nを止めずに運用する:アップデート・バックアップ・エラー通知・脆弱性」に書いています。
応用
PostgreSQLに替える
新しく立てるなら、compose.yaml に公式の例どおり postgres:18 のサービスとデータ用ボリュームを足し、n8nの environment に次を足します。
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
PostgreSQL側の PGDATA: /var/lib/postgresql/data の行は消しません。18はデータの置き場所の既定が変わったので、この行がないとボリュームの外に書かれ、作り直したときDBが空になります。PostgreSQLにしても暗号鍵は .n8n に入るので、そのボリュームは残します。
SQLiteの本番から替えるときは、n8n export:workflow --backup と n8n export:credentials --backup で書き出し、新しい側でオーナーを作ってから import で入れ直します。ユーザーや実行履歴は移らず、ワークフローは止まった状態で入ります。
できないことと、その代わり
| できないこと | 代わりの手 |
|---|---|
| セルフホストでは「Sign in with Google」(Managed OAuth2)が使えない | Google Cloud ConsoleでOAuthクライアントを作り、Client IDとClient Secretを入れる |
| npmで入れるとn8n Assistantが使えない。npm導入は3.0で廃止予定 | Dockerで入れる |
| 止まっている間に来たWebhookや、過ぎたスケジュールは取り戻せない | 外から死活を見張る。フォーム側でも送信内容をメールで控える |
| SQLiteからPostgreSQLへの自動移行はない | CLIで書き出して入れ直す |
| CLIのバックアップにはユーザー・実行履歴・変数・設定が入らない | .n8n のボリュームごと残す |
| 安いVPSにAssistantのサンドボックスまでは載らない | 本体だけ立てる。使うならメモリ4GB以上にする |
Googleの「Testing」状態で利用者の種類が「External」のOAuthアプリは、同意とトークンが7日で切れます。社内のGoogle Workspaceだけで使うなら「Internal」にします。
よく詰まるところ
| 症状 | 原因 | 直し方 |
|---|---|---|
WebhookのURLが http://localhost:5678/ で始まる | N8N_WEBHOOK_URL がない、または変更後に作り直していない | 設定して docker compose up -d |
Google連携で redirect_uri_mismatch | Google側の戻り先とOAuth Redirect URLが違う(http/https、ポート) | Credential画面のURLをそのまま貼り直す |
| 開くと応答がない | 80・443が閉じている | ファイアウォールで許可する |
| 証明書が「TRAEFIK DEFAULT CERT」で警告が出る | DNS未反映・向き先違い・443が閉じている | dig で向き先を見て、docker compose logs traefik を読む |
| ファイルを読み書きするノードで権限エラー | local-files をrootで作った | docker compose exec n8n id の番号で chown する |
| スケジュールが夜中に動く | GENERIC_TIMEZONE がない | Asia/Tokyo にして作り直す |
| Codeノードの日時がずれる | task-runners にタイムゾーンを渡していない | task-runners にも GENERIC_TIMEZONE と TZ を足す |
| 作り直したらCredentialが全部使えない | ボリュームを消した、または鍵が変わった | 控えた鍵を N8N_ENCRYPTION_KEY に戻す |
| IPアドレスで開くとログインできない | ログインのCookieはHTTPSでしか送られない | 独自ドメインのHTTPSで開く |
| 起動し直したらワークフローが消えた | down -v でボリュームを消した、またはフォルダ名を変えた | -v を付けない。フォルダ名を戻す |
| Webhookのパスが使用中で公開できない | 同じパスとメソッドが別のワークフローにある | パスかメソッドを変える |
| nginx経由で編集画面が固まる | WebSocketが転送されていない | Upgrade と Connection を転送する |
次にやること
- n8nの使い方入門:最初のワークフローを作って動かす
- n8nで外部サービスとつなぐ:Webhook・HTTP Request・API
- n8nを止めずに運用する:アップデート・バックアップ・エラー通知・脆弱性
- n8nの料金:Cloudとセルフホストの費用、商用利用の線引き