ERPNext VPS Docker 自行代管部署指南
在 VPS 上以 Docker 執行 ERPNext,了解 11 個容器的配置、TLS、寄信、版本固定與還原測試,並避免 exit code 137 的記憶體不足故障。
執行此部署代表的責任
在 VPS 上自行代管 ERPNext 是維運工作,不是執行一個指令就能完成的安裝作業。官方 Docker Compose stack 包含 11 個容器,並保存總帳與客戶資料。因此,以下每個步驟都必須採用更嚴格的標準:備份必須實際還原過,才能算是備份;未固定版本的 image tag 則可能隨時觸發 schema migration。
本文會反覆使用幾個名稱。ERPNext 是商務應用程式。Frappe 是其底層的 Python framework。Bench 是管理 sites 的 command line 工具,已預先安裝在容器內。site 代表一個 tenant:包含一個 MariaDB database,以及一個存放上傳檔案的目錄。本文幾乎所有指令都會在 backend 容器內,以 bench 對其中一個指定的 site 執行。
本指南使用 frappe_docker repository,這是專案維護的部署版本。以下所有指令都已在 2026 年 8 月針對該 repository 完成驗證。如果你不熟悉 Docker Compose,在 VPS 上執行 Docker Compose 會說明本指南預設你已具備的基礎知識。
ERPNext 需要多大的 VPS?
The data behind this chart
[
{
"label": "Evaluation",
"vcpu": 2,
"ram_gb": 4,
"disk_gb": 40
},
{
"label": "Small production",
"vcpu": 4,
"ram_gb": 8,
"disk_gb": 100
},
{
"label": "Room to grow",
"vcpu": 4,
"ram_gb": 16,
"disk_gb": 160
}
]已發布的建議是,在第一位使用者登入前,至少準備 2 個 vCPU 和 4 GB RAM。這是評估等級。這些數值是起始點,不是本指南實測的結果;實際需求取決於您的文件量。最後一列根本不是已發布的最低需求,而是記憶體大致不再成為主要考量的範圍。
請如實評估小型方案。1 GB 或 2 GB 的 VPS 可以啟動整個堆疊,但第一次匯入資料或執行較長的報表時就會停止運作,因為 9 個長時間執行的容器、MariaDB 的 buffer pool,以及建立報表的 Python worker,無法容納在這麼少的記憶體中。這類故障不會平順發生。核心的 out of memory killer 會停止某個容器,接著 docker inspect 會顯示 "OOMKilled": true,結束代碼為 137。worker 若在工作中途遭到終止,已提交的文件可能只完成一半背景處理。
對每天使用 ERPNext 的公司而言,8 GB RAM、4 個 vCPU,以及 100 GB SSD,是較務實的最低配置。通常會先耗盡 RAM。磁碟空間的成長速度也比預期快,因為每個附件和每份本機備份,都會與資料庫儲存在同一個磁碟區上。
11 個容器及其用途
堆疊啟動且 9 個容器正在執行後,執行 docker compose ps。另外兩個容器 configurator 和 create-site 只會執行一次後結束,因此總數為 11 個。
backend透過 gunicorn 執行 Frappe 應用程式。bench就是在這裡執行。frontend是 nginx。它提供靜態資源,並將其他所有請求轉送到後端。queue-short和queue-long是 RQ(Redis Queue)worker。它們執行寄送電子郵件、匯入資料及建立報表等背景工作。scheduler執行依時間排程的工作,包括排程報表及自動重複文件。websocket是瀏覽器即時更新所使用的 socket.io 程序。db是 MariaDB。redis-cache和redis-queue是兩個獨立的 Redis 執行個體,分別用於快取及工作佇列。
這種拆分方式值得了解,因為它能告訴你應查看哪個日誌。電子郵件卡在佇列中是 worker 問題,因此 docker compose logs -f queue-short 才是正確的指令。頁面可以載入,但通知徽章始終不更新,則是 websocket 問題。針對這兩種問題查看 backend 日誌,只會浪費一個下午。
使用 production compose 檔案安裝,不要使用 demo
此 repository 提供 pwd.yml,README 也明確說明:「此設定僅供短期評估使用。您無法在此設定中安裝自訂應用程式。」您可以用它花一個下午熟悉 ERPNext,但不要用它執行公司的正式環境。
sudo apt update && sudo apt install -y git
curl -fsSL https://get.docker.com | bash
git clone https://github.com/frappe/frappe_docker
cd frappe_docker
mkdir -p ~/gitops
cp example.env ~/gitops/erpnext.env開啟 ~/gitops/erpnext.env,修改 4 個值。ERPNEXT_VERSION 固定 image tag。範例檔案中的 DB_PASSWORD 會以 123 提供。SITES_RULE 是 Traefik 的路由規則,LETSENCRYPT_EMAIL 用來接收憑證警告。
ERPNEXT_VERSION=v16.32.1
DB_PASSWORD=<a long random password>
SITES_RULE=Host(`erp.example.com`)
LETSENCRYPT_EMAIL=ops@example.com現在產生一個 compose 檔案,然後啟動它。
docker compose --project-name erpnext \
--env-file ~/gitops/erpnext.env \
-f compose.yaml \
-f overrides/compose.mariadb.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.https.yaml \
config > ~/gitops/erpnext.yaml
docker compose --project-name erpnext -f ~/gitops/erpnext.yaml up -dconfig 不會啟動任何服務。它會合併基礎檔案與覆寫檔案,並列印已完成所有變數替換的結果。接著使用產生的檔案啟動服務。這個額外步驟很有價值:執行中的 stack 會以單一檔案呈現,您可以直接閱讀並提交至版本控制,因此即使有人修改 env 檔案,或您從 repository 拉取更新,設定也不會在執行期間自行變更。Docker Compose 多個檔案的合併方式 詳細說明覆寫規則。
等待 db 啟動,並等待 configurator 結束。這需要幾秒鐘。接著建立 site。
docker compose --project-name erpnext exec backend \
bench new-site --mariadb-user-host-login-scope=% \
--db-root-password '<your DB_PASSWORD>' \
--install-app erpnext \
--admin-password '<a strong admin password>' \
erp.example.com檢查結果:
docker compose --project-name erpnext ps
docker compose --project-name erpnext exec backend bench --site erp.example.com list-appslist-apps 應列出 frappe 與 erpnext 及其版本。正常的 ps 會顯示 9 個服務處於 running 狀態,且沒有服務處於 restarting 狀態。
這裡經常會出現 2 個問題。--mariadb-user-host-login-scope=% 在 Docker 中不是選擇性設定。應用程式容器會透過 Docker network 連線至 MariaDB,因此對資料庫而言,它是來自遠端主機;限定為 localhost 的資料庫使用者無法從該處登入。建立 site 時會失敗,並顯示 MariaDB access denied 錯誤,指出 root 使用者。% 範圍會允許新 site 的使用者從該私有 network 上的任何主機存取。
第二個問題是 site 名稱。前端預設會根據 HTTP Host 標頭選擇要提供的 site,因此即使兩者都存在,以 erpnext 建立的 site 也無法透過 erp.example.com 存取。請依照上面的方式,使用網域名稱作為 site 名稱;或者在 env 檔案中將 FRAPPE_SITE_NAME_HEADER 設為 site 名稱,然後再次產生 compose 檔案。
HTTPS,以及啟用前必須滿足的條件
compose.https.yaml 覆寫設定會讓 Traefik 在 443 埠執行、將 80 埠重新導向至 443,並向 Let's Encrypt 申請憑證。TLS(傳輸層安全性)可避免發票內容與工作階段 cookie 以純文字形式在網路上傳輸。
必須同時滿足兩項條件,否則永遠不會核發憑證。erp.example.com 的 DNS A 記錄必須已指向 VPS。由於 Let's Encrypt 會透過 80 埠的 HTTP-01 challenge 驗證你是否控制該網域,因此 80 和 443 埠都必須能從網際網路連線。請同時檢查供應商的網路防火牆與伺服器上的防火牆。這是兩個獨立的控制項,而控制面板中的防火牆最容易被忽略。
憑證會儲存在 cert-data volume 的 /letsencrypt/acme.json。如果瀏覽器顯示預設憑證而不是你的憑證,請在 docker compose --project-name erpnext ps 找出 proxy 服務名稱,並檢查其日誌中的 ACME(自動憑證管理環境)錯誤。要在同一台伺服器上執行其他 Web 應用程式嗎?由一個 Traefik 執行個體代理多個 Docker Compose 應用程式說明如何共用 proxy,避免爭用 443 埠。像這樣的伺服器上,第二個應用程式通常會直接面向客戶,而自架 Chatwoot 客服服務台也會位於同一個 proxy 後方,因此處理發票的人員可以在同一處回覆客戶電子郵件與聊天訊息。
外寄電子郵件,否則發票永遠無法送出
這是大多數 ERPNext 指南會略過的步驟,也是決定系統是否實用的關鍵。沒有正常運作的外寄郵件,發票無法送達客戶,密碼重設郵件不會抵達,排程報表也無法傳送。此堆疊不包含郵件伺服器。
不要嘗試從 VPS 直接透過埠 25 寄信。大多數供應商會封鎖新帳戶的外寄埠 25;即使郵件成功送出,也會因為新 VPS 位址沒有寄件信譽而遭拒收,或被歸入垃圾郵件。請使用已驗證的 relay,並透過埠 587 傳送。
支援的方式是在 ERPNext 介面中使用 Email Account 畫面,系統會以加密方式儲存密碼。您也可以將這些鍵值寫入 site config:
docker compose --project-name erpnext exec backend \
bench --site erp.example.com set-config mail_server smtp.example.com
docker compose --project-name erpnext exec backend \
bench --site erp.example.com set-config mail_port 587 --parse
docker compose --project-name erpnext exec backend \
bench --site erp.example.com set-config use_tls 1 --parse
docker compose --project-name erpnext exec backend \
bench --site erp.example.com set-config mail_login 'erp@example.com'
docker compose --project-name erpnext exec backend \
bench --site erp.example.com set-config auto_email_id 'erp@example.com'--parse 會將 587 儲存為數字,而不是字串 "587"。讀回檔案並確認這兩個值前後都沒有引號:
docker compose --project-name erpnext exec backend \
cat sites/erp.example.com/site_config.json請透過 Email Account 畫面設定 mail_password,不要在命令列設定。這樣系統會以加密方式儲存,且不會寫入 shell 歷程記錄。
接著傳送一封實際郵件。建立 Sales Invoice,將其寄送到您可控制的地址,並在傳送期間監看佇列:
docker compose --project-name erpnext logs -f queue-short外寄郵件會以背景工作執行,因此郵件未送達時,通常會在該日誌中顯示為失敗的工作,而不是在瀏覽器中顯示錯誤。也請為寄件網域發布 SPF(sender policy framework)與 DKIM(domainkeys identified mail)記錄,然後新增 DMARC 政策。缺少這些設定時,即使發票本身技術上正確,仍可能落入客戶的垃圾郵件資料夾。如果您希望自行管理完整寄送流程,自架 Mailcow 郵件伺服器可提供您控制的 relay,並可將其部署在與 ERP 分開的伺服器上。
可實際還原的備份
單獨的資料庫傾印不算 ERPNext 的備份。附件與私有檔案位於 sites 目錄,而不在 MariaDB 中。只還原資料庫時,每個已上傳的採購單都會變成失效連結。
docker compose --project-name erpnext exec backend \
bench --site erp.example.com backup --with-files這會在 sites volume 內的 sites/erp.example.com/private/backups 寫入 4 個檔案:
-database.sql.gz傾印檔-files.tar公開檔案的封存檔-private-files.tar私有檔案的封存檔-site_config_backup.jsonsite 設定的副本
第 4 個檔案最常被丟棄,但它也是造成問題的關鍵。該檔案包含 encryption_key,這是 Frappe 用來加密儲存密碼的金鑰,包括電子郵件帳戶認證資訊、付款閘道金鑰,以及所有整合服務的 secret。還原資料庫時若沒有相符的金鑰,網站仍可正常載入,但寄送郵件會失敗:
frappe.exceptions.ValidationError: Encryption key is invalid! Please check site_config.json務必始終將這 4 個檔案放在一起。
接著將它們移出伺服器。儲存在 volume 內的備份無法在伺服器故障後保留,而且 bench 也會清理這些檔案:預設會刪除該目錄中超過 24 小時的備份。
docker compose --project-name erpnext cp \
backend:/home/frappe/frappe-bench/sites/erp.example.com/private/backups \
~/erpnext-backups從 cron 執行這項工作,然後將目錄推送到不由你管理的位置。加密的 restic 異地備份 是適合的工具,因為它會在上傳前加密資料,而 restic check 可確認儲存庫仍可讀取。ERP 備份是整本帳冊的副本,因此應以靜態加密形式儲存在另一台硬體上,而不是這台伺服器。
在需要之前先測試還原
未經測試的備份只是猜測。將它還原到同一台主機上的第二個站台,絕不要還原到正式站台。
docker compose --project-name erpnext exec backend \
bench new-site --mariadb-user-host-login-scope=% \
--db-root-password '<your DB_PASSWORD>' \
--admin-password '<a strong admin password>' \
restore-test.example.com
docker compose --project-name erpnext exec backend \
bench --site restore-test.example.com --force restore \
sites/erp.example.com/private/backups/<stamp>-erp.example.com-database.sql.gz \
--with-public-files sites/erp.example.com/private/backups/<stamp>-erp.example.com-files.tar \
--with-private-files sites/erp.example.com/private/backups/<stamp>-erp.example.com-private-files.tar \
--db-root-password '<your DB_PASSWORD>'將備份設定中的加密金鑰複製到還原後的站台,否則其整合功能會持續失效:
docker compose --project-name erpnext exec backend \
bench --site restore-test.example.com set-config encryption_key '<value from site_config_backup.json>'接著以會計人員的方式檢查還原結果。開啟應收帳款報表,並將期末餘額與正式站台比較。開啟最近的採購發票,並下載其附件。站台能顯示登入頁面,完全不能證明還原成功。
完成後移除測試站台:
docker compose --project-name erpnext exec backend \
bench drop-site restore-test.example.com為何版本固定對 ERPNext 更重要
在靜態網站上,未固定的映像標籤代表意外重新啟動。在 ERPNext 上,這代表執行結構描述遷移。bench migrate 會重寫資料庫資料表,也可能改寫文件資料,而且無法復原。回復方式是從備份還原,不是執行 docker compose down。
因此,請固定映像標籤。ERPNEXT_VERSION=v16.32.1 是該儲存庫在 2026 年 8 月自有 pwd.yml 中固定的版本。未經確認,不要直接沿用這個版本號。目前的版本列在 frappe/erpnext 發行版本頁面,現有的映像標籤則列在 Docker Hub。升級前,請先閱讀目標版本的變更說明。
升級程序應先建立備份並啟用維護模式。
docker compose --project-name erpnext exec backend \
bench --site erp.example.com backup --with-files
docker compose --project-name erpnext exec backend \
bench --site erp.example.com set-maintenance-mode on編輯 ERPNEXT_VERSION 中的 ~/gitops/erpnext.env,然後產生設定、擷取映像並執行遷移。
docker compose --project-name erpnext \
--env-file ~/gitops/erpnext.env \
-f compose.yaml \
-f overrides/compose.mariadb.yaml \
-f overrides/compose.redis.yaml \
-f overrides/compose.https.yaml \
config > ~/gitops/erpnext.yaml
docker compose --project-name erpnext -f ~/gitops/erpnext.yaml pull
docker compose --project-name erpnext -f ~/gitops/erpnext.yaml up -d
docker compose --project-name erpnext exec backend \
bench --site erp.example.com migrate
docker compose --project-name erpnext exec backend \
bench --site erp.example.com set-maintenance-mode off維護模式很重要,因為 migrate 執行時會修改結構描述。若使用者在資料表遷移到一半時提交文件,最後可能必須手動修復記錄。
一次只升級一個主要版本,並在每個步驟之間建立備份。某個版本的遷移程式是針對從前一個版本升級而撰寫的,因此跳過主要版本會以未經測試的組合執行遷移。
該儲存庫也提供 overrides/compose.migrator.yaml,會加入一個每次啟動時執行 bench --site all migrate 的容器。這很方便,但也表示只要 docker compose up 使用了變更後的標籤,就可能在無人監看的情況下遷移正式環境資料庫。對商務系統而言,應將 migrate 視為當天確認後才執行的決策。
強化存放客戶記錄的伺服器
第一次登入時變更 Administrator 密碼。評估用 compose 檔案會將 admin 設為該密碼,而這種習慣可能一路帶進正式環境。
將 DB_PASSWORD 變更為不同於 123(位於 example.env)的值。該值會以純文字出現在產生的 ~/gitops/erpnext.yaml 中,因此請 chmod 600 該檔案,並避免將它放入任何 git repository。若需要更強的作法,overrides/compose.mariadb-secrets.yaml 會從 Docker secret 檔案讀取密碼,而不是使用環境變數。在 Docker Compose 中處理環境檔案與 secret 說明其中的取捨。
只公開必要的項目。使用 HTTPS override 時,只有 80 和 443 埠會對外公開。不要在 db 服務中加入 ports 對應,讓資料庫用戶端更容易連線;這會使 MariaDB 暴露在公用網際網路上。請改用 docker compose --project-name erpnext exec backend bench mariadb。在主機上允許 22、80 和 443,拒絕其餘連線,並同時檢查供應商另外提供的網路防火牆。
對所有具備 System Manager 角色的帳戶,在 System Settings 中啟用雙因素驗證。該角色可以讀取所有文件並匯出所有資料表,因此應將它視為管理員帳戶,而不是為了方便而授予的角色。如果執行多個自架應用程式,將 Authentik 作為自架單一登入提供者 比每個應用程式各使用一組密碼更合適。
為主機安裝修補程式,並在更新 kernel 後重新開機。在依賴該 stack 自動恢復前,先檢查產生的檔案,確認每個服務都設定了 restart policy;沒有此設定的 stack 會在重新開機後維持停止狀態。讓 Docker Compose stack 在重新開機後再次啟動 說明 systemd 相關設定。
當 ERPNext 在單一 VPS 上開始無法應付時
一台 VPS 可以長期支撐小型公司運作。以下跡象表示它已經開始無法應付:
- 背景工作逐漸堆積,導致電子郵件與匯入作業延遲數分鐘甚至數小時。
docker inspect回報容器出現"OOMKilled": true或結束代碼 137。- 原本耗時 2 秒的報表現在需要 30 秒,而 MariaDB 是佔用 CPU 的程序。
- 備份執行時間過長,導致一次備份與下一次排程執行重疊。
先為 MariaDB 提供不與其他服務共用的資源,因為資料庫與 Python worker 會競用相同的記憶體,而 buffer pool 是最需要更多記憶體的部分。擴充應用程式伺服器的效果通常不如預期。在 Docker 或主機上執行資料庫說明這項取捨,而在 Docker Compose 中設定記憶體限制則能避免單一容器耗盡其他容器的資源。
接著增加 queue worker,而不是擴充 web capacity。ERPNext 的慢速工作都在背景執行,例如報表產生與大量匯入。增加 worker 容器的成本低於升級更大型的主機,而且能直接解決使用者實際抱怨的問題。
FAQ
VPS 需要多少 RAM 才能執行 ERPNext?
發布的建議從 4 GB RAM 搭配 2 vCPU 開始,但此配置僅適合評估。公司若每天使用,建議配置 8 GB RAM、4 vCPU,以及 100 GB SSD。低於此配置時,kernel 的 out-of-memory killer 會在負載增加時停止容器。docker inspect 會將此事件記錄為 "OOMKilled": true,退出碼為 137。這些數值只是起始點,不是實際測量結果,因此請在第一個月持續監控實際記憶體使用量。
可以在 production 執行 pwd.yml 嗎?
不可以。專案的 README 說明,pwd.yml 僅供短期評估使用,也指出無法在其中安裝自訂 app。請使用 compose.yaml 搭配 MariaDB、Redis 和 HTTPS 覆寫設定,再使用 docker compose config 將設定 render 成單一檔案,並執行該檔案。
為什麼建立 ERPNext site 後立即無法連線?
前端預設會依 HTTP Host 標頭選擇要提供的 site,因此 site 名稱必須與瀏覽器中的網域相符。以 erpnext 建立的 site 不會在 erp.example.com 提供服務。請以網域作為 site 名稱建立 site,或在 env 檔案中將 FRAPPE_SITE_NAME_HEADER 設為 site 名稱,重新 render compose 檔案,再重新啟動 stack。
ERPNext 備份必須包含哪些內容?
必須將以下 4 個檔案放在一起:-database.sql.gz dump、-files.tar 和 -private-files.tar 封存檔,以及 -site_config_backup.json 設定副本。執行 bench --site erp.example.com backup --with-files 會產生這 4 個檔案。設定副本包含 encryption_key。若還原時缺少該副本,儲存的整合密碼將無法解密,並顯示為 Encryption key is invalid! Please check site_config.json。
如何升級 ERPNext 才不會損壞資料?
先使用 --with-files 備份,啟用 maintenance mode,在 env 檔案中修改 ERPNEXT_VERSION,重新 render compose 檔案,執行 pull,啟動 stack,接著執行 bench --site erp.example.com migrate,最後關閉 maintenance mode。每次只跨越 1 個 major version,並先閱讀 release notes,因為 migrate 會重寫 schema 和文件資料,且無法復原。若要 rollback,必須還原一開始建立的備份。