SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 已更新 2026-08-30

如何使用 Docker 在 VPS 自架 n8n 並設定 HTTPS

本指南教您使用 Docker Compose 與 Postgres 在 VPS 部署 n8n。解決常見的 WEBHOOK_URL 設定錯誤、加密金鑰遺失問題,並確保反向代理正確處理 HTTPS 流量,避免容器因記憶體不足遭 OOM killer 終止。

您將建置的系統

n8n 是一款工作流程自動化工具:透過視覺化編輯器,讓觸發條件(如 Webhook、排程或表單提交)啟動一連串節點,進而呼叫 API、重塑資料並寫入其他系統。由於它能與各種模型供應商及資料庫對接,且無需撰寫程式碼,因此已成為 AI 代理工作流程的標準整合工具。透過一個 docker run 指令,兩分鐘內即可取得可用的編輯器。本指南將著重於其餘 90% 的實作:將預設的 SQLite 檔案替換為 Postgres 以提升耐用性、透過 HTTPS 存取,以及最容易出錯的部分——確保 Webhook 能提供外部網路可存取的 URL。

最終架構為運行於同一個 Docker 網路中的兩個容器:n8n 本體,以及儲存工作流程與憑證的 Postgres 資料庫。主機上的反向代理負責處理 TLS termination 並將請求轉發至 localhost 的 n8n,確保所有流量皆經由代理層進入,無服務直接暴露於網際網路。此架構可與 2026 自架服務清單 中的其他服務並存。

先決條件與實際限制

您需要一台至少具備 1 GB RAM 的 VPS;若工作流程開始實際運作,請規劃 2 GB RAM,因為執行程序加上 Node.js 執行環境會消耗大量記憶體,若因記憶體不足導致容器在執行中被 OOM killer 終止,將會是極為糟糕的學習經驗。單核心 vCPU 起步即可。若此伺服器還需執行其他較吃資源的服務,請優先考量該服務的需求:照片庫通常是主要負擔,PhotoPrism 與 Immich 的實際 RAM 最低需求遠高於 n8n 的需求。媒體伺服器亦同:Jellyfin 伺服器加上如 Halcyon(將媒體庫重建成 90 年代錄影帶出租店風格) 這類瀏覽前端,在 n8n 察覺到資源壓力前,就會先佔用掉 RAM 與轉碼所需的餘裕。

您需要一個網域或子網域(例如 n8n.example.com),並設定 A 記錄指向 VPS 的公開 IP,且必須在申請憑證前完成解析。連接埠 80 與 443 必須對代理伺服器開放;n8n 本身的連接埠 5678 絕對不可直接暴露於網際網路。您需要安裝 Docker Engine 與 Compose 外掛程式;若執行 docker compose version 時出現 docker: 'compose' is not a docker command 錯誤,代表您安裝的是舊版獨立二進位檔,請改用 sudo apt install docker-compose-plugin

測試環境可用 SQLite,正式環境請使用 Postgres

n8n 預設的資料庫是位於 /home/node/.n8n/database.sqlite 的 SQLite 檔案。若只是為了初步測試,不掛載 volume 即可使用,但容器一旦重建資料就會遺失,這本身就是一項教訓。改用 Postgres 的原因並非單純為了原始效能,而是因為 SQLite 採用單一寫入鎖定機制;當一個執行個體同時運行多個工作流程,或是您最終會用到的佇列模式(queue mode)時,在高併發下會拋出 SQLITE_BUSY: database is locked 錯誤。Postgres 沒有這類限制,且能透過 pg_dump 進行乾淨的備份,這也是 n8n 官方文件針對正式伺服器所建議的配置。若日後才要切換,必須手動遷移資料,因此若此伺服器具備重要性,請直接從 Postgres 開始部署。

DNS 與防火牆

請先設定 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 埠。docker-compose 檔案已將 n8n 綁定至 127.0.0.1:5678,僅限主機上的反向代理可存取該服務,若設定 ufw allow 5678 將會破壞此隔離機制。

Compose 檔案

建立一個工作目錄並新增一個 docker-compose.yml。這包含了整個堆疊,包含兩個服務、一個私有網路以及兩個具名儲存卷。

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 容器內部,後者僅代表 n8n 本身)。帶有 condition: service_healthydepends_on 可防止 n8n 在開機時與 Postgres 發生競態;若無此設定,n8n 會在啟動後因找不到資料庫而退出。位於 /home/node/.n8n 的具名儲存卷 n8n_data 用於存放加密金鑰,若使用 SQLite 則包含資料庫檔案,這是絕對不能遺失的目錄。請將映像檔版本鎖定在特定版本,切勿使用 latest;原因請參閱下方的升級章節。

Secrets 檔案

請勿將密碼寫入 compose 檔案。請將其放入旁邊的 .env 檔案中,讓 Compose 自動讀取,並透過產生方式確保其具備隨機性。

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 使用此金鑰加密了第一個憑證,變更該金鑰將導致所有憑證無法解密,因此請務必在初始設定時設定一次,之後切勿更動該行。

決定 Webhook 是否運作的環境變數

有四個變數控制 n8n 如何向外部宣告自身資訊,設定錯誤是 n8n 最常見的技術支援問題。

  • N8N_HOST 是公開主機名稱,即 n8n.example.com。若在代理伺服器後方仍維持預設值 localhost,編輯器會嘗試從您的瀏覽器讀取自身的 API 來源 localhost,這將導致失敗。
  • N8N_PROTOCOL=https 告知 n8n 服務透過 TLS 提供,因此它會將 session cookie 標記為 Secure 並建立 https:// 網址。
  • 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/...。這些網址看起來合理且不會報錯,但從網際網路無法存取,導致呼叫端的請求會靜默失敗。請將其設定為完整的公開基礎網址(包含結尾斜線),並確認 Webhook 節點顯示的網址不包含埠號。

N8N_PROXY_HOPS=1 告知 n8n 的 Express 伺服器信任前方的一台代理伺服器,確保速率限制及任何讀取客戶端 IP 的功能都能取得真實位址,而非代理伺服器的位址。您刻意不應在此設定的變數是 N8N_RUNNERS_ENABLED:自 1.69 版本起,在獨立沙盒處理程序中執行 Code-node 邏輯的任務執行器(task runners)已成為預設值,且在本指南所鎖定的 2.x 版本中為強制要求,因此舊有的選擇性啟用方式已被棄用。若現在設定此變數,n8n 只會記錄一則通知,要求您將其移除。

首次啟動

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

健康的首次啟動應以 Editor is now accessible via: 行結束,且上方應有 n8n ready on ..., port 5678 行。docker compose ps 應顯示兩個容器 Up,並將 postgres 標記為 (healthy)。若 n8n 陷入 Restarting 迴圈,請閱讀日誌;這通常是資料庫連線或下文所述的儲存卷權限問題。

使用反向代理配置 TLS

n8n 本身透過 5678 埠使用純 HTTP 協定;需在前端配置 HTTPS 終止。以下提供兩種簡潔的選擇。

若您已運行多個容器,可將 n8n 部署在 自動簽發 TLS 憑證的 Traefik 反向代理 後方。只需設定幾個標籤(labels),Traefik 就會自動為您申請並續期憑證。

若此伺服器僅運行此應用程式,使用 nginx 虛擬主機搭配 Let's Encrypt 憑證會更簡單。請參考 適用於 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;
    }
}

UpgradeConnection "upgrade" 標頭為必要設定。n8n 透過 WebSocket 將即時執行更新推送到編輯器,若缺少這兩行,登入頁面載入後會因連線中斷而卡住。X-Forwarded-Proto $schemeN8N_PROXY_HOPS=1 的配套設定:它會告知 n8n 原始請求為 HTTPS,即使代理伺服器是透過純 HTTP 與其連線;此舉可避免 n8n 誤判連線不安全而拒絕其自身的 Cookie。proxy_read_timeout 3600 則能防止長時間執行的任務被 nginx 預設的 60 秒逾時限制所中斷。

建立您的第一個工作流程,使其運作

開啟 https://n8n.example.com/,建立擁有者帳號(詳見下一節),並建構一個能驗證路徑是否暢通的最小工作流程:包含 Webhook 輸入、HTTP 呼叫以及回應輸出。

  1. 新增一個 Webhook 節點。將方法設為 POST,路徑設為類似 hello 的值。此節點會顯示兩個 URL:Test URLProduction URL,這也是導致一半「我的 Webhook 無法運作」回報的原因。Test URL 僅在您點擊 Listen for test event 後回應一次呼叫,隨後即失效。Production URL 則會在工作流程設為 Active 時隨時回應。
  2. 在其後新增一個 HTTP Request 節點,指向任何公開的 JSON API。對 https://api.github.com/zen 執行 GET 請求並取得單行字串即可。
  3. 新增一個 Respond to Webhook 節點,並將 Webhook 節點的 Respond 選項設為 "Using Respond to Webhook node",以便呼叫端能接收到 HTTP 節點的輸出。
  4. 將工作流程切換為 Active(右上角)並進行呼叫:curl -X POST https://n8n.example.com/webhook/hello。您應能收到該禪語行,這包含了 POST 輸入、API 呼叫與回應輸出,這也是大多數實際自動化流程的架構。

排程變體會將 Webhook 節點替換為 Schedule Trigger,並改為呼叫模型端點。使用 在同一台 VPS 上執行的 Ollama 進行自架,是建構每日摘要工具的簡潔方式。

使用者管理,而非基本驗證

舊版的 n8n 指南會要求您設定 N8N_BASIC_AUTH_ACTIVE=true。這些變數已在 n8n 1.0 版本中移除,目前已無作用。現今的驗證機制採用擁有者帳號 (owner account):當您首次載入編輯器時,n8n 會要求您建立一組電子郵件與密碼作為擁有者,此步驟為強制性,不再提供匿名模式。請在首次啟動後立即建立帳號,再將 URL 提供給他人:在 docker compose up 與完成首次表單提交之間,任何存取該實例的人皆可將其據為己有。在反向代理層加裝基本驗證 (basic auth) 是合理的額外防護,但這僅屬於第二層驗證,並非真正的身分驗證機制。本指南中的擁有者帳號與其他功能皆適用於免費的社群版;若您日後需要具備細部權限的角色管理或 SSO 功能,建議在規劃前先閱讀 哪些 n8n 功能需要付費授權

備份:先處理加密金鑰,再處理資料庫

有兩項資料需要備份,且兩者的可替代性並不相同。

N8N_ENCRYPTION_KEY。您在 n8n 中儲存的每一項憑證、API 權杖、資料庫密碼與 OAuth 密鑰,皆以此金鑰進行靜態加密(encryption at rest)。若沒有這把金鑰,Postgres 中的工作流程將毫無用處:若將資料庫還原至使用不同金鑰的新伺服器,n8n 將無法解密任何憑證,且無法復原或重設。您的 .env 檔案存放著此金鑰;請在建立金鑰當天,將其複製到伺服器以外的地方,存入密碼管理員是理想的做法。這是最關鍵的備份。

Postgres 資料庫,用於存放工作流程、執行紀錄以及加密後的憑證本身:

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

請排程執行上述指令,並將備份檔複製到伺服器外。若要在全新的 VPS 上還原:請先啟動一次 stack 以建立資料庫,接著停止 n8n,使用 psql 載入備份檔,將相同的 N8N_ENCRYPTION_KEY 放入 .env,最後啟動 n8n。擁有相同的金鑰加上備份檔,即可運作實例;若使用新金鑰,則工作流程將無法使用任何憑證。

升級:鎖定標籤

此 compose 檔案刻意鎖定 n8nio/n8n:2.29.10 而非 latest。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

重大版本跳躍(Major-version jumps)是此處最需注意的部分。例如 2.0 系列預設將 N8N_BLOCK_ENV_ACCESS_IN_NODE 切換為 true,導致任何讀取 process.env 的 Code 節點在設定回 false 之前會靜默失效;該版本同時開始強制執行設定檔的嚴格權限。在跨越重大版本邊界前,請務必閱讀 2.0 重大變更頁面。n8n 會在啟動時自動執行必要的資料庫遷移,這正是升級前執行 pg_dump 為必要步驟的原因。由於憑證透過 .env 中的金鑰加密,且資料儲存於 Postgres,容器本身是可拋棄的:升級方式為替換容器,若需復原,則鎖定至先前的標籤並還原資料庫備份即可。

失敗模式與對應訊息

The requested webhook "POST hello" is not registered. 呼叫 Webhook 時若工作流未處於 Active 狀態,或在無人監聽時呼叫測試路徑,會收到 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 顯示 :5678localhost 節點顯示 https://n8n.example.com:5678/webhook/...http://localhost:5678/...。這是因為 WEBHOOK_URL 未設定或設定錯誤,導致 n8n 使用 N8N_HOST:N8N_PORT 而非您的公開基礎網址來建構位址。請設定 WEBHOOK_URL=https://n8n.example.com/,並使用 docker compose up -d 重建容器,連接埠號即會消失。

瀏覽器出現 There was a problem loading init data 編輯器已載入但無法連線至後端 API。在代理伺服器後方,這通常是因為 N8N_HOSTWEBHOOK_URL 設定錯誤、代理伺服器遺漏 WebSocket Upgrade 標頭,或是 N8N_PROTOCOL 與您的連線方式不符。請確認四個對外變數,並確保代理伺服器已轉發 UpgradeConnection

日誌出現 password authentication failed for user "n8n" 且容器不斷重啟。 n8n 發送的密碼與資料庫初始化時的密碼不符。陷阱在於:Postgres 僅在初始化「空」資料目錄時才會讀取 POSTGRES_PASSWORD。若您啟動堆疊後才在 .env 修改 POSTGRES_PASSWORD,現有的 postgres_data 儲存卷仍會保留舊密碼。請將其改回原始密碼;若無須保留資料,請 docker compose downdocker 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,通常是因為直接存取 IP 與連接埠而非透過 HTTPS 代理。請改用 https://n8n.example.com/ 存取。除非確實無法使用 HTTPS,否則不應設定 N8N_SECURE_COOKIE=false,且絕對不要在對外開放的伺服器上這樣做。

若要在工作流中加入語言模型,請參閱 使用 Claude 與 n8n 建構 AI 工作流

FAQ

我應該為 n8n 選擇 SQLite 還是 Postgres?

SQLite(預設值)適合試用 n8n 或執行單一工作流的個人實例。若有正式用途,請改用 Postgres:SQLite 的單一寫入鎖定機制在並發時會拋出 database is locked 錯誤,而 Postgres 可透過 pg_dump 進行乾淨的備份。後續遷移需手動執行,因此若該環境重要,請直接從 Postgres 開始。

為什麼我的 n8n webhook 從未觸發?

原因幾乎都是 WEBHOOK_URL 設定錯誤。若未設定或設定錯誤,n8n 會根據 N8N_HOST:N8N_PORT 產生 webhook 位址,其中常包含 :5678localhost,這些位址看起來有效,但網際網路無法存取,導致呼叫方的請求無法送達。請設定 WEBHOOK_URL=https://n8n.example.com/ 並確認節點顯示的 URL 不包含埠號。第二個原因是呼叫了未切換為 Active 的工作流,這會回傳 The requested webhook ... is not registered.

我必須備份 n8n 的哪些資料?

兩項資料。首先是 .env 檔案中的 N8N_ENCRYPTION_KEY,因為所有儲存的憑證皆以此加密,遺失將導致憑證永久無法解密,請在建立當天將其複製到伺服器外。其次是 Postgres 資料庫的 pg_dump,用於儲存工作流、執行歷史與憑證。還原時兩者缺一不可:必須同時具備相同的金鑰與資料庫傾印檔。

如何將 n8n 部署在 HTTPS 之後?

n8n 透過 5678 埠提供純 HTTP 服務;請在前方部署反向代理以終止 TLS。將 n8n 綁定至 127.0.0.1:5678 以確保僅代理伺服器可存取,接著使用具備自動憑證功能的 Traefik,或使用 nginx 搭配 Let's Encrypt 憑證。設定 N8N_PROTOCOL=httpsWEBHOOK_URL=https://your-host/,並確保代理伺服器轉發 WebSocket Upgrade 標頭,否則編輯器會卡住。

如何安全地升級 n8n?

請鎖定特定的映像檔標籤(tag)而非使用 latest,並在升級前進行 pg_dump,因為 n8n 會在啟動時自動執行遷移。請閱讀發行說明以了解重大變更,接著更新標籤並執行 docker compose pull n8n && docker compose up -d n8n。容器是可拋棄的,若需復原,請鎖定回先前的標籤並還原升級前的資料庫傾印檔。